关于这个问题,有很多程序员不明白,并且在日常编程中经常会用到。比如:我在方法体外声明了一个变量,然后我想到一个方法体内改变
这个值,然后到另外一个方法体内使用这个值。当然在我以前的一篇文章《基础:Out, params,ref 细说并沉淀》中有提到过,可以使用ref,或者Out来实现,只是当时没有将这个概括出来。没有将这个方法改成成以引用传递引用类型。
接下来分别说明:
先定义这么一个Person类
1: #region 按值传递引用类型 定义的一个Person类
2: public class Person {
3: private string _personName;
4: private int _personAge;
5:
6: #region Constructors
7: public Person(string name, int age) {
8: this.PersonAge = age;
9: this.PersonName = name;
10: }
11: public Person() {
12: }
13: #endregion
14:
15: #region Properties
16: public string PersonName {
17: get { return _personName; }
18: set { _personName = value; }
19: }
20: public int PersonAge {
21: get { return _personAge; }
22: set { _personAge = value; }
23: }
24: #endregion
25:
26: public void Display() {
27: Console.WriteLine("Name:{0},Age:{1}", this.PersonName, this.PersonAge);
28: }
29: }
30: #endregion
然后创建一个方法,将Person类的实体以值类型的形式传递进去
1: static void SendAPersonByValue(Person p) {
2: //改变p的年龄
3: p.PersonAge = 99;
4: //调用者能不能看到在方法体内的重新构造的对象?
5: p = new Person("YeanJay", 24);
6: }
这样我们在Main中可以这样来调用这个方法:
1: //按照值传递引用类型
2: Person p = new Person("Ken", 23);
3: Console.WriteLine("调用方法之前:");
4: p.Display();
5: SendAPersonByValue(p);
6: Console.WriteLine("调用方法之后:");
7: p.Display();
执行后的结果如下图:
这样可以看出,PersonAge的属性被改变了,似乎看起来违反了“按值”传递的语义。但是,这里传入的是引用,在复制的过程中,复制了指向调用者的对象的引用。
在调用SendAPersonByValue()方法与调用者指向同一个对象,所以可以改变状态数据,但是在调用的方法体内,重新实例化,却是不可以的。
接下来看下按照引用传递引用类型:
同样使用上面的那个类,只是定义一个如下的调用方法:
1: #region 按照引用传递引用类型
2: static void SendAPersonByRef(ref Person p) {
3: p.PersonAge =27;
4: p = new Person("YeanJay", 24);
5:
6: }
7: #endregion
这样在Main中将SendAPersonByValue(p);改成SendAPersonByRef(ref p);
调用后的结果如下图:
显然,在使用ref之后,按照引用传递引用类型,在方法中可以改变传入的引用在内存中的指向。
以下是Andrew Troelsen书中《Pro C# 2008 and .net 3.5 platform》中的关于按引用传递引用类型时候的黄金法则:
1.如果按引用传递引用类型,被调用者可能改变对象的状态的值和所引用的对象;
If a reference type is passed by reference, the callee may change the values of the object’s
state data as well as the object it is referencing.
2.如果按值传递引用类型,被调用者可能改变对象的状态数据的值,但不能够改变所引用的对象。
If a reference type is passed by value, the callee may change the values of the object’s state
data but not the object it is referencing.