关键字Ref和Out本质区别实例分析

学过C/C++的人,对C#的关键字RefOut应该都很好理解。它们都提供了一种可以在被调用函数内修改传递的参数的值的方法。因为这一功能很类似C/C++的指针。对于没学过C/C++的,也应该可以明白这两个参数的作用。

  虽然RefOut都提供了修改参数值的方法,但它们还是有一点点小的区别

  1、Ref在作为参数调用函数之前,变量一定要赋值,否则会得到一个常规编译错误:使用了未赋值的变量。

  2、在被调用函数内,以Ref引入的参数在返回前不必为它赋值。

  3、Out在作为参数调用函数之前,变量可以不被赋值。

  4、在被调用函数内,以Out引入的参数在返回前一定要至少赋值一次。

  其实本质上讲,Ref更适合理解为给被调用函数传递了一个与原参考同地址的变量。而Out则可以理解为在调用函数前,先给变量找个地方,让被调用函数在给定地点放一个值。

  看上去很简单不是吗?确实如此,这里是一个例子:

namespace StudyAndTest

  {

  /** <summary>

  /// Summary description for Class1.

  /// </summary>

  class Class1

  {

  /** <summary>

  /// The main entry point for the application.

  /// </summary>

  [STAThread]

  static void Main(string[] args)

  {

  int m_temp        =0;    //Must be assigned to before call any mothed with the variable by reference.

  Console.WriteLine("Int data befor change:{0}",m_temp);

  ChangeData1(ref m_temp);

  Console.WriteLine("Int data after change:{0}",m_temp);

  ChangeData2(out m_temp);

  Console.WriteLine("Int data after change:{0}",m_temp);

  }

  static void ChangeData1(ref int i_ref)

  {

  Console.WriteLine("Int data in ChangeData1:{0}",i_ref);

  i_ref    = 1;

  }

  static void ChangeData2(out int i_ref)

  {

  //Console.WriteLine("Int data in ChangeData2:{0}",i_ref);    //Error in building, use of unassigned local variable i_ref

  i_ref    = 2;    //The out parament i_ref must be assigned to before control leaves the current mothod.

  }

  }

  }

然而C#毕竟是与C/C++有着不同之处的。这就是在C#内,所有的变量被分为两类:值类型和引用类型。

  那么我们就会有这样的问题:将Ref和Out分别应用于引用类型和值类型的变量上,会是什么样的结果呢?

  对于应用于值类型数据的情况,上面的例子已经完全讨论过了,就是完全遵守上面的四句话。而对于引用类型数据,有一个很有趣的问题,就是默认情况下(不带Ref也不带Out)它是以Ref情况而调用函数,即上面的四句话仍然满足。

  看这样的一个例子:

namespace StudyAndTest

  {

  /** <summary>

  /// Summary description for Class1.

  /// </summary>

  class Class1

  {

  /** <summary>

  /// The main entry point for the application.

  /// </summary>

  [STAThread]

  static void Main(string[] args)

  {

  TempClass m_class1    = new TempClass();

  m_class1.m_member    = 0;

  Console.WriteLine("i_obj data before changeData3 :{0}",m_class1.m_member);

  ChangeData3(m_class1);

  Console.WriteLine("i_obj data after changeData3 :{0}",m_class1.m_member);

  }

  static void ChangeData3(TempClass i_obj)

  {

  i_obj.m_member    = 3;

  //            Console.WriteLine("i_obj data in ChangeData3:{0}",i_obj.m_member);

  }

  }

  class TempClass

  {

  public int m_member;

  }

  }

这让人感觉就是Ref,确实如此,默认就是在以Ref为引用类型在调用函数,所以还是要注意以下问题:

  引用类型数据一定要初始化。而至于引用类型自己初始化的问题,就交给该类型自己了。如上面的问题,m_member在没有赋值前,一样可以编译的,但运行一定就不对了(但有默认值),这是因为TempClass里没有构造函数。在被调用函数内,一样的使用参数,而且所有对引用参数的改变都影响到函数外。这是默认的情况。

  但如果我们强行加上Ref或者Out关键字,会是什么结果呢???

  1、如果是用Ref,那么结果是和什么都没用一样!即默认就是用的Ref。(让我们少打了几个字符)

  2、如果是用Out,那么要遵守上面的3,4原则,即:在调用前,不必初始化引用对象,再简单一点:就是可以不用New一个对象。但在函数内,返回前一定要New一个,并且在New之前,参数对象是不能使用的。

  也就是上面说到的,Out只是在调用前分配了一个地点,在调用函数中使用该地点。注意:这里“地点”一词决不是内存地址。

  再思考一个问题:如果在使用Out参考时,在调用函数前,我们已经New了一个对象,再来调用函数结果会是什么呢?

  你将“丢失”一部份内存(如果在C/C++里,一定是这样的)。也就是说,在调用了函数后,函数里New的一个对象会让函数外的对象丢失,而新的对象在函数内有效,在函数外也有效。幸运的是:原来的对象的内存并不会像C/C++那样完全的丢失,它将由垃圾回收器来管理了。所以我们并不担心内存的真正丢失问题(这真是一件值得庆幸的事)。

  看这样的例子:

 class Class1

  {

  /** <summary>

  /// The main entry point for the application.

  /// </summary>

  [STAThread]

  static void Main(string[] args)

  {

  TempClass m_class1    = new TempClass();

  m_class1.m_member    = 0;

  Console.WriteLine("i_obj data before changeData3 :{0}",m_class1.m_member);

  ChangeData3(out m_class1);

  Console.WriteLine("i_obj data after changeData3 :{0}",m_class1.m_member);

  }

  static void ChangeData3(out TempClass i_obj)

  {

  i_obj    = new TempClass();

  i_obj.m_member    = 3;

  //            Console.WriteLine("i_obj data in ChangeData3:{0}",i_obj.m_member);

  }

  }

  class TempClass

  {

  public int m_member;

  }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值