代码如下: public class Foo { public string F1 { get;set;} public int F2 { get; set; } } class Program { static void Main(string[] args) { Foo foo = new Foo(); foo.F1 = "A"; foo.F2 = 1; Test(foo); Console.WriteLine("F1={0},F2={1}", foo.F1, foo.F2); } private static void Test(Foo foo) { Foo tmp = new Foo(); tmp.F1 = "F1"; tmp.F2 = 20; foo = tmp; } }
代码的输出是什么呢???
输入结果:
F1=A,F2=1
为什么输出不是F1=F1,F2=20呢?
楼上已经说的很好了,我补充一下。
在进入方法 Test 之前,变量 foo 的地址和值分别如下图所示:
在进入方法 Test 之后并且实例化局部变量 tmp 之前,变量 foo 的地址和值分别如下图所示:
可以看到变量 foo 的地址已经变了,而值尚未改变,局部变量 tmp 也已经初始化了,只是尚未赋值,接着再往下看。
当实例化 tmp 后各变量的值如下所示:
这一步除了实例化 tmp 外没有其他操作。
然后再看 tmp 赋值给 foo 后是什么情况:
foo 地址没变,值变成 tmp 的值了。
接着跳出 Test,看看又是神马情况:
额,foo 的地址和值都变成进入 Test 之前的了,也就是说我们在 Test 里对 foo 地址和值所做的改变都无效,对于输出也就不难理解了。
这个和 C++ 里对指针的操作是一样的,在一个方法里要对指针引用做修改,只需要传递指针进来就够了,但如果要对指针本身做修改就需要传递指针的地址(即指针的指针)进来才行了,因为指针地址本身也只是一个值类型的整数。
以上是我的理解,不正确的还请多包涵。
C#函数的参数如果不加ref,out这样的修饰符显式申明参数是通过引用传递外,默认都是值传递.
你修改传递过来的值,也只能作用于传递过来的这个值。
如果你修改传递过来的引用,就可以作用到原来的值了。
这个算是c#的一个坑吗?
见到过一个swap的写法如下:
public static void Swap(Foo a, Foo b) { Foo tmp = a; a = b; b = tmp; }
写的人认为Foo是按引用传递,在子函数里面交换了形参,实参也交换了。这样解释是否更合理:对于不加ref或out的参数,函数调用中发生的数据传送是单向的。 即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参的值不会变化。
最近看swift中的的参数传值,有三种传参方式let var inout,就明确避免了这类问题。
因为在方法里foo变量引用的实例变了,但是在Main里的实例还是原来那个
如果你在方法里不new一个新的实例,而是直接更改foo的属性F1、F2的值,那么就可以看到输出的值为F1=F1,F2=20
首先,参数foo必然是引用传递,因为C#除了明确的值类型,其他类型都是引用传递。
第二,你在test中是对参数值进行修改,C#中,只要不表明ref/out,则对参数值的修改将只在函数内部有效。你在test中new了一个新的tmp,然后将其值赋给参数foo,这就是对foo这个参数的修改。
而Swap函数中并没有对参数进行修改,而是对引用类型内部的值进行修改,这时就会影响这个引用的原始数据。
其实这个和C的指针是一样的,函数参数是指针还是指针的指针,ref相当于指针的指针