Coding with passion

首页 新随笔 联系 订阅 管理
  14 Posts :: 0 Stories :: 113 Comments :: 1 Trackbacks

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

C# 中的变量

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

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

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

public struct MyStruct
{
  public int a;
  public int b;
}
MyStruct m1 = new MyStruct();

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

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

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

// ----------------------------------------
// MyClass definition
public class MyClass
{
  public int Value;
}


// ----------------------------------------
// Tester methods
public static void TestRef(ref MyClass m1)
{
  // 这里的 m1 也就相当于大家所说的指向指针的指针:
  // m1 指向 Main 中的 m,而 m 则指向那个实际的 MyClass 的实例
  // 相当于 C++ 中的
  // MyClass** m1 = &m;
  // (*m1)->Value = 10;
  //
  m1.Value = 10;
}

public static void TestNoRef(MyClass m1)
{
  // 这里是一个普通的传递引用类型的例子。相当于:
  // MyClass m1 = m;
  // m1.Value = 20;
  //
  // m1 复制了 m 中的内容,也就是说现在 m1 也和 m 一样,指向了 m 指向的实例
  // 所以这里对 m1 的修改也会影响到 Main 中的 m。
  //
  m1.Value = 20;
}

public static void TestCreateRef(ref MyClass m1)
{
  // 这里的 m1 也是一个指向引用的引用:
  // m1 指向 Main 中的 m,而 m 则指向那个实际的 MyClass 的实例
  // 相当于 C++ 中的
  // MyClass** m1 = &m;
  // *m1 = new MyClass();
  // (*m1)->Value = 100;
  // 在上面的 *m1 = new MyClass() 这个调用的时候,实际上只是将 Main 中 m 的值(引用)给修改了,
  // 也就是说现在 m1 指向 Main 中的 m,而 m 现在则指向了这个新生成的实例。
  // 所以这里 m1.Value = 100 是会影响到 Main 中的结果的
  //
  m1 = new MyClass();
  m1.Value = 100;
}

public static void TestCreateNoRef(MyClass m1)
{
  // 在这个方法里面,我们新申明了一个 MyClass 的实例,而让 m1 指向了这个实例
  // 这时候,实际上将 m1 的值修改了, m1 和 Main 中的 m 各自指向不同的实例
  // 所以对 m1 做的任何修改都不会影响到 m 了
  //
  m1 = new MyClass();
  m1.Value = 200;
}

public static void Main()
{
  MyClass m = new MyClass();
  m.Value = 1;

  TestRef(ref m);
  Console.WriteLine(m.Value);

  TestNoRef(m);
  Console.WriteLine(m.Value);

  TestCreateRef(ref m);
  Console.WriteLine(m.Value);

  TestCreateNoRef(m);
  Console.WriteLine(m.Value);
}
posted on 2007-09-02 19:07 Kellin 阅读(2853) 评论(15)  编辑 收藏

Feedback

#1楼  2007-09-02 19:24 周银辉      
代码显示似乎有点问题
  回复  引用  查看    

#2楼  2007-09-02 19:42 飘过 [未注册用户]
css没弄好~~
  回复  引用    

#3楼 [楼主] 2007-09-02 20:45 Kellin      
怎么我这儿看着没什么问题 :(
  回复  引用  查看    

#4楼  2007-09-02 20:48 wanlim0817 [未注册用户]
理解了,好文章,以后多写点哈:)谢谢
  回复  引用    

#5楼  2007-09-02 21:36 Dark [未注册用户]
路过~~~
  回复  引用    

#6楼  2007-09-02 21:36 Danio Zhu [未注册用户]
Good
  回复  引用    

#7楼  2007-09-03 09:03 邓高建      
谢过!
  回复  引用  查看    

#8楼  2007-09-03 09:35 丹心猪(Dansinge)      
还是没看懂有ref和没ref的区别在哪
  回复  引用  查看    

#9楼  2007-09-03 11:54 Wu.Country@侠缘      
把我的一个旧帖拿出来讨论一下吧,这里我出了一点错误,大家评论后,我才明白是什么回事了,希望对大家有用!
http://www.cnblogs.com/WuCountry/archive/2006/02/27/338514.html
  回复  引用  查看    

#10楼  2007-09-03 12:04 yxin1322 [未注册用户]
很容易理解,如果仅仅是改变传入对象的成员字段,使用和不使用ref是一样的。而当函数内使用了new重新创建对象,而又想将这种变化带出函数,就需要使用ref了
  回复  引用    

#11楼  2007-09-03 17:21 glory [未注册用户]
不错
  回复  引用    

#12楼  2007-09-03 20:51 WXWinter(冬)      
//帮你补充一个例子,(^_^)
class Class1
{
static void Main(string[] args)
{
a obj1=new a();
a obj2=new a();

x(obj1);
y(ref obj2);

System.Console.Read();

}

static void x(a obj)
{
obj=null;
System.GC.Collect();
}
static void y(ref a obj)
{
obj=null;
System.GC.Collect();
}
}

class a
{
~a()
{
System.Console.WriteLine("析构了");
}
}
  回复  引用  查看    

#13楼  2007-09-03 21:17 Kellin      
析构函数不太建议使用。不过在这个简单的程序中,倒是问题不大,可以从侧面说明一些问题,呵呵
  回复  引用  查看    

#14楼  2007-09-04 00:23 壁虎 [未注册用户]
@Wu.Country@侠缘
@Kellin

拜读了两位大作,终于明白了ref 的实现机制了。谢谢。
  回复  引用    

在不跨AppDomain的情况下,有ref 对于 引用类型和 没有Ref是一样的,但是如果在AppDomain不在同一个的时候,如.netRemoting引用的时候,对于有ref 和没有 ref 的引用类型对象,最好写多个例子,这样可以有更深刻的了解
  回复  引用    


标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-10-06 18:59 编辑过
 
另存  打印