一直以来对于在引用类型前加ref传递变量和不加ref传递引用类型变量的区别不是很清楚,最近看到网上一个朋友给的一道题,仔细思索和编写代码测试,终于明白其中区别了。现整理如下,以供自己和朋友们理解和记忆。
ref是使变量通过引用的方式传递,而引用类型本身也是通过引用方式传递变量,所以一直纠结在这两者的区别?经过思考、查证资料以及编写代码证实,两者的区别在于:
引用类型前不加ref时,只能修改所引用对象的值,而不能对所引用对象的引用进行更改。
而在引用类型前加ref时,不仅可以修改所引用对象的值,也可以改变对所引用对象的引用。
通过下面测试代码,能够很好的反映出来。代码中使用了非安全代码(指针),通过使用指针显示对象的内存地址,可以观察出对对象引用的变化。
不加ref传递:
1 class Value
2 {
3 public int i = 15;
4 }
5 class Program
6 {
7 static unsafe void Main(string[] args)
8 {
9 Program t = new Program();
10 t.first();
11 }
12 public unsafe void first()
13 {
14 int i = 5;
15 Value v = new Value();
16 v.i = 25;
17
18 ///查看V实例的地址
19 fixed (int* pi = &(v.i))
20 {
21 Console.WriteLine("V.i Address 0x{0:X}", (int)pi);
22 }
23
24 second(v, i);
25
26 ///执行完second函数后V实例的地址
27 fixed (int* pi = &(v.i))
28 {
29 Console.WriteLine("V.i Address 0x{0:X}", (int)pi);
30 }
31
32 Console.WriteLine(v.i);
33 }
34 public unsafe void second(Value v, int i)
35 {
36 i = 0;
37 v.i = 20;
38
39 ///重新指定前V实例的地址
40 fixed (int* pi = &(v.i))
41 {
42 Console.WriteLine("V.i Address 0x{0:X}", (int)pi);
43 }
44
45 Value val = new Value();
46 v = val; ///创建一个v的副本,指向val,因此v的值不会被val改变
47
48 ///val实例的地址
49 fixed (int* pi = &(val.i))
50 {
51 Console.WriteLine("val.i Address 0x{0:X}", (int)pi);
52 }
53
54 ///v的副本地址
55 fixed (int* pi = &(v.i))
56 {
57 Console.WriteLine("V.i Address 0x{0:X}", (int)pi);
58 }
59 Console.WriteLine(v.i + " " + i + " ");//15,0 可以理解
60 }
61 }
运行结果如下:
在这里,由于不能改变对V引用对象的引用,因此在执行“V=Val”时,其实是创建了一个V对象的副本对象,然后将副本对象的引用指向Val对象。
加ref传递:
class Value
{
public int i = 15;
}
class Program
{
static unsafe void Main(string[] args)
{
Program t = new Program();
t.first();
}
public unsafe void first()
{
int i = 5;
Value v = new Value();
v.i = 25;
///查看V实例的地址
fixed (int* pi = &(v.i))
{
Console.WriteLine("V.i Address 0x{0:X}", (int)pi);
}
second(ref v, i);
///执行完second函数后V实例的地址
fixed (int* pi = &(v.i))
{
Console.WriteLine("V.i Address 0x{0:X}", (int)pi);
}
Console.WriteLine(v.i);
}
public unsafe void second(ref Value v, int i)
{
i = 0;
v.i = 20;
///重新指定前V实例的地址
fixed (int* pi = &(v.i))
{
Console.WriteLine("V.i Address 0x{0:X}", (int)pi);
}
Value val = new Value();
v = val; ///v直接指向val,v断开了与之前连接
///val实例的地址
fixed (int* pi = &(val.i))
{
Console.WriteLine("val.i Address 0x{0:X}", (int)pi);
}
///v的地址
fixed (int* pi = &(v.i))
{
Console.WriteLine("V.i Address 0x{0:X}", (int)pi);
}
Console.WriteLine(v.i + " " + i + " ");//15,0 可以理解
}
}
运行结果如下:
在这里的话,由于加了ref传递对象V,因此在执行“V=Val”时,V对象直接指向了Val对象,因此V对象的地址不再0x12D16A4,而是Val的地址0x12D36CC。