正确理解 C# 中的 ref 关键字 (续)

本文深入探讨了C#中的ref关键字使用方法及其背后原理,包括值类型与引用类型的内存处理区别,通过具体代码示例解释了ref如何改变变量传递方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

出处:http://www.cnblogs.com/Kellin/archive/2007/09/02/879084.html

前几天写了一篇介绍 ref 使用方法的文章:正确理解 C# 中的 ref 关键字,用于帮助大家加深对 ref 使用方法的了解。看来还是有部分兄弟姐妹没有完全搞明白:( 那我就再加上一篇,讲述得清楚一些。有不对的地方请指出,大家共同提供,呵呵。

C# 中的变量

依据网上的一些文章介绍,要了解 C# 对内存的处理,首先要了解 C# 中的变量,以及变量的值是什么。在 C# 中,一个变量仅仅用于连接一个名称(这个名称当然就是变量名了,我们在代码中用到的)和一小块内存。一个变量有它的值,也就是这小块内存中存储的值。至于这小块内存的大小,以及如何解释其中存储的数据,则是和变量的类型(值类型和引用类型)相关的;这也就是值类型和引用类型的区别所在。

一个引用类型的变量的值永远是 null 或者一个引用。如果是引用,则它一定会指向一个相对应的对象实例。比如:一个申明为 Stream s 的变量一定会是 null 或者指向一个 Stream 类的实例(当然也可能是 FileStream, MemoryStream 等子类的实例)。而这个变量的大小则永远是一个引用所需的大小,和其指向实例的大小是没有关系的。在 32 位系统上,引用的大小是 4 个字节。

而值类型则不同!一个值类型变量的值(所对应内存块中存储的数据)则永远是其对应实例的值。比如,我们定义了一个 struct:


 
1
public struct MyStruct
2
{
3
   public int a;
4
   public int b;
5
}
6
MyStruct m1 =  new MyStruct();

在这里,变量 m1 的值则是两个整数,而不是一个指向两个整数的引用。其内存中的存储的数据大小也就是 8 个字节,而不是引用所需的 4 个字节了。

讲到这里,大家也就会明白为什么值类型是按值传递(copy整个对象中的数据,对新对象数据的修改是不会影响到原对象的),而引用类型是按引用传递(仅仅 copy 的是引用--暂时可以把它当作内存地址吧)了!而不仅仅是看它们各自的名称,呵呵。在 C# 中,值类型因为都是按值传递,所以也就不存在对象生命周期,引用计数啊什么的。在 unsafe 的情况下,我们甚至还可以像 C++ 中的那样申明值类型的指针!但是对于引用类型,这确是不行的。我想,C#中,引用类型的对象都是由系统来管理的;如果再允许我们用指针来指来指去的,整个程序非得乱套了不可!

那么我们再来看看上次那些 C# 的代码


 
1
// ----------------------------------------
2
// MyClass definition
3
public class MyClass
4
{
5
   public int Value;
6
}
7
 
8
 
9
// ----------------------------------------
10
// Tester methods
11
public static void TestRef( ref MyClass m1)
12
{
13
   // 这里的 m1 也就相当于大家所说的指向指针的指针:
14
   // m1 指向 Main 中的 m,而 m 则指向那个实际的 MyClass 的实例
15
   // 相当于 C++ 中的
16
   // MyClass** m1 = &m;
17
   // (*m1)->Value = 10;
18
   //
19
  m1.Value = 10;
20
}
21
 
22
public static void TestNoRef(MyClass m1)
23
{
24
   // 这里是一个普通的传递引用类型的例子。相当于:
25
   // MyClass m1 = m;
26
   // m1.Value = 20;
27
   //
28
   // m1 复制了 m 中的内容,也就是说现在 m1 也和 m 一样,指向了 m 指向的实例
29
   // 所以这里对 m1 的修改也会影响到 Main 中的 m。
30
   //
31
  m1.Value = 20;
32
}
33
 
34
public static void TestCreateRef( ref MyClass m1)
35
{
36
   // 这里的 m1 也是一个指向引用的引用:
37
   // m1 指向 Main 中的 m,而 m 则指向那个实际的 MyClass 的实例
38
   // 相当于 C++ 中的
39
   // MyClass** m1 = &m;
40
   // *m1 = new MyClass();
41
   // (*m1)->Value = 100;
42
   // 在上面的 *m1 = new MyClass() 这个调用的时候,实际上只是将 Main 中 m 的值(引用)给修改了,
43
   // 也就是说现在 m1 指向 Main 中的 m,而 m 现在则指向了这个新生成的实例。 
44
   // 所以这里 m1.Value = 100 是会影响到 Main 中的结果的
45
   //
46
  m1 = new MyClass();
47
  m1.Value = 100;
48
}
49
 
50
public static void TestCreateNoRef(MyClass m1)
51
{
52
   // 在这个方法里面,我们新申明了一个 MyClass 的实例,而让 m1 指向了这个实例
53
   // 这时候,实际上将 m1 的值修改了, m1 和 Main 中的 m 各自指向不同的实例
54
   // 所以对 m1 做的任何修改都不会影响到 m 了
55
   //
56
  m1 = new MyClass();
57
  m1.Value = 200;
58
}
59
 
60
public static void Main()
61
{
62
  MyClass m =  new MyClass();
63
  m.Value = 1;
64
    
65
  TestRef( ref m);
66
  Console.WriteLine(m.Value);
67
    
68
  TestNoRef(m);
69
  Console.WriteLine(m.Value);
70
    
71
  TestCreateRef( ref m);
72
  Console.WriteLine(m.Value);
73
    
74
  TestCreateNoRef(m);
75
  Console.WriteLine(m.Value);
76
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值