在C#中通过引用或值传递对象

在C#中,我一直认为非原始变量是通过引用传递的,原始值是通过值传递的。

所以当传递给一个方法的任何非原始对象时,对该方法中的对象做的任何事情都会影响被传递的对象。 (C#101的东西)

但是,我注意到,当我传递一个System.Drawing.Image对象,这似乎并不是这样的情况? 如果我将system.drawing.image对象传递给另一个方法,并将图像加载到该对象上,那么让该方法超出范围并返回调用方法,该图像不会加载到原始对象上?

为什么是这样?

对象根本不通过。 默认情况下,参数被评估,并且它的被作为你正在调用的方法的参数的初始值按值传递。 现在重要的一点是,该值是引用类型的参考 – 一种获取对象(或null)的方法。 该对象的更改将从调用者可见。 但是,如果使用按值传递(这是所有类型的默认值),则更改参数的值以引用不同的对象将可见。

如果要使用传递ref ,则无论参数类型是值类型还是引用类型,都必须使用outref 。 在这种情况下,变量本身实际上是通过引用传递的,所以参数使用与参数相同的存储位置 – 调用者可以看到参数本身的变化。

所以:

 public void Foo(Image image) { // This change won't be seen by the caller: it's changing the value // of the parameter. image = Image.FromStream(...); } public void Foo(ref Image image) { // This change *will* be seen by the caller: it's changing the value // of the parameter, but we're using pass by reference image = Image.FromStream(...); } public void Foo(Image image) { // This change *will* be seen by the caller: it's changing the data // within the object that the parameter value refers to. image.RotateFlip(...); } 

我有一篇文章,里面有更多的细节 。 基本上,“通过参考”并不意味着你的想法。

还有一个代码示例:

 void Main() { int k = 0; TestPlain(k); Console.WriteLine("TestPlain:" + k); TestRef(ref k); Console.WriteLine("TestRef:" + k); string t = "test"; TestObjPlain( t); Console.WriteLine("TestObjPlain:" +t); TestObjRef(ref t); Console.WriteLine("TestObjRef" + t); } public static void TestRef(ref int i) { i = 5; } public static void TestPlain(int i) { i = 5; } public static void TestObjRef(ref string s) { s = "TestObjRef"; } public static void TestObjPlain(string s) { s = "TestObjPlain"; } 

而输出:

TestPlain:0

TestRef:5

TestObjPlain:测试

TestObjRefTestObjRef

你是如何将对象传递给方法的?

你是否在做对象的新方法,如果你必须在方法中使用ref。

以下链接给你更好的主意。

http://dotnetstep.blogspot.com/2008/09/passing-reference-type-byval-or-byref.html http://msdn.microsoft.com/en-us/library/0f66670z(vs.71)。; ASPX#vclrfpassingmethodparameters_referencetypes

当您将System.Drawing.Image类型对象传递给一个方法时,您实际上是将该引用的副本传递给该对象。

所以如果在这个方法中,你正在加载一个新的图像,你正在使用新的/复制的引用加载。 所以你不要在信号上做出改变

 YourMethod(System.Drawing.Image image) { //now this image is a new reference //if you load a new image image = new Image().. //you are not changing the original reference you are just changing the copy of original reference } 

当你这样做的时候,我想它更清楚。 我建议下载LinkPad来测试这样的事情。

 void Main() { var Person = new Person(){FirstName = "Egli", LastName = "Becerra"}; //Will update egli WontUpdate(Person); Console.WriteLine("WontUpdate"); Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n"); UpdateImplicitly(Person); Console.WriteLine("UpdateImplicitly"); Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n"); UpdateExplicitly(ref Person); Console.WriteLine("UpdateExplicitly"); Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n"); } //Class to test public class Person{ public string FirstName {get; set;} public string LastName {get; set;} public string printName(){ return $"First name: {FirstName} Last name:{LastName}"; } } public static void WontUpdate(Person p) { //New instance does jack... var newP = new Person(){FirstName = p.FirstName, LastName = p.LastName}; newP.FirstName = "Favio"; newP.LastName = "Becerra"; } public static void UpdateImplicitly(Person p) { //Passing by reference implicitly p.FirstName = "Favio"; p.LastName = "Becerra"; } public static void UpdateExplicitly(ref Person p) { //Again passing by reference explicitly (reduntant) p.FirstName = "Favio"; p.LastName = "Becerra"; } 

这应该输出

WontUpdate

名字:埃格利,姓:贝塞拉

UpdateImplicitly

名:Favio,姓:Becerra

UpdateExplicitly

名:Favio,姓:Becerra

接受的答案听起来有点错误和混乱。 什么是“参考副本”?

下列陈述如何有意义?

“但是,当您使用按值传递(这是所有类型的默认值)时,更改参数的值以引用不同的对象将不可见。” 按值传递不是所有类型的默认值。

他的链接中的示例尝试将对象的实例设置为null。 由于自动垃圾收集,该对象未成功设置为空。 这种方式不能被删除。

这是一篇比较Java和C#的微软文章。

https://msdn.microsoft.com/en-us/library/ms836794.aspx

“所有对象都是引用

引用类型与C ++中的指针非常相似,特别是在将标识符设置为新的类实例时。 但是当访问这个引用类型的属性或方法时,使用“。” 运算符,这类似于访问在堆栈上创建的C ++数据实例。 所有的类实例都是使用new运算符在堆上创建的,但是不允许删除,因为两种语言都使用自己的垃圾回收机制,下面讨论。

在通过引用传递你只在函数参数中添加“ref”,并且由于main是静态的( public void main(String[] args) ),所以你应该声明函数“static”

 namespace preparation { public class Program { public static void swap(ref int lhs,ref int rhs) { int temp = lhs; lhs = rhs; rhs = temp; } static void Main(string[] args) { int a = 10; int b = 80; Console.WriteLine("a is before sort " + a); Console.WriteLine("b is before sort " + b); swap(ref a, ref b); Console.WriteLine(""); Console.WriteLine("a is after sort " + a); Console.WriteLine("b is after sort " + b); } } }