按值和按引用传递参数
假设有以下两个类型
类A和结构B,它们包含的成员一样,都有一个属性X,也都有一个改变X属性的方法,参数为结构或类本身的实例。方法都将X的值改为2,更改之后,X的值将会是?
代码如下:
1 /// <summary> 2 /// 创建一个类A 3 /// </summary> 4 public class A 5 { 6 public int X { get; set; } 7 8 public void ChangeA(A a) 9 { 10 a.X = 2; 11 } 12 } 13 /// <summary> 14 /// 创建一个结构B 15 /// </summary> 16 public struct B 17 { 18 public int X { get; set; } 19 20 public void ChangeB(B b) 21 { 22 b.X = 2; 23 } 24 }
实例化且执行以下代码后,A和B下的X的值将会是?
var a = new A { X = 1 }; a.ChangeA(a); var b = new B { X = 1 }; b.ChangeB(b);
结果:
a.X = 2, b.X=1;
因为:
A为类,属于引用类型,ChangeA方法的参数和其创建的A实例为同一个引用,但凡有一个被改变了,同一个引用下的其它类也会被改变!所以当赋值X=2时,X的最终值也就变了。
B为结构,属于值类型,ChangeB方法的参数和其创建的B实例都被分配到不同的栈(stack)中,所以即使赋值X=2,实例a的X还是等于1,因为他们都是值类型,在相同内存类型的位置,存储在不同的地方。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ref参数 -- 引用传递,顾名思义,引用同一个内存存储。
使用ref修饰符修改B结构里面的ChangeB方法,通过引用传递变量,使其方法参数和B的实例b指向同一个引用。
代码修改如下:
1 /// <summary> 2 /// 创建一个结构B 3 /// </summary> 4 public struct B 5 { 6 public int X { get; set; } 7 /// <summary> 8 /// 引用传递变量 9 /// </summary> 10 /// <param name="b">使用了ref传递变量</param> 11 public void ChangeB(ref B b) 12 { 13 b.X = 2; 14 } 15 } 16 // 此时 17 var b = new B 18 { 19 X = 1 20 }; 21 b.ChangeB(ref b); 22 // 此时 b.X = 2;因为使用ref使得ChangeB参数作为引用传递变量
如果给类A的ChangeA方法最一下修改,在方法里重新创建一个对象,且将X的值赋值为3
代码如下:
1 /// <summary> 2 /// 创建一个类A 3 /// </summary> 4 public class A 5 { 6 public int X { get; set; } 7 /// <summary> 8 /// 使用ref修饰符 9 /// </summary> 10 /// <param name="a"></param> 11 public void ChangeA(A a) 12 { 13 // 重新赋值X,此时与外部创建的实例a属于同一个引用 14 a.X = 2; 15 // 此时我们在堆上创建一个新的对象,此时外部a.X的最终值是?? 16 a = new A { X = 3 }; 17 } 18 } 19 var a = new A 20 { 21 X = 1 22 }; 23 a.ChangeA(ref a); 24 // 最终a.X == 2,因为a是还与外部实例a指向了同一个引用,所以依旧是2 a = new A { X = 3 } 只是在内存推上创建了一个新对象
但加上ref修饰符之后呢:
/// <summary> /// 创建一个类A /// </summary> public class A { public int X { get; set; } /// <summary> /// 使用ref修饰符 /// </summary> /// <param name="a"></param> public void ChangeA(ref A a) { // 重新赋值X,此时与外部创建的实例a属于同一个引用 a.X = 2; // 此时我们在堆上创建一个新的对象,此时外部a.X的最终值是?? a = new A { X = 3 }; } } var a = new A { X = 1 }; a.ChangeA(ref a); // a.X =3 因为ref修饰符传递了对引用的引用,所以 在赋值 a = new A { X =3 }时,其实是修改了最开始a实例的引用,并没有创建新的对象,所以 a.X =3
好玩~