按值和按引用传递参数

假设有以下两个类型

类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

好玩~

posted @ 2020-04-12 18:26  VTing4You  阅读(368)  评论(0编辑  收藏  举报