C#中,数据传递的方式有两种:按值传递和按引用传递,且默认情况下都是按值传递。而CLR支持两种基本类型为:值类型和引用类型。
1. 当传递的参数为值类型,且数据传递方式为按值传递时,传递的是数据的一个拷贝。
2. 当数据传递方式为按引用传递时,必须以ref或者out关键字来修饰。由于这部分不是本文主题,不做详细介绍,想了解相关内容可参见【原创】C#中ref和out的异同
3. 当引用类型参数按值传递时,传递的是引用类型地址的数据拷贝。
下面通过例子来验证第3点中所说
先看以下示例代码:
class Program
{
public static void Main()
{
var abf = new ArgsByRef();
var abf1 = abf;
AddRef(abf);
Console.WriteLine(abf.i);
ReplaceRef(abf1);
Console.WriteLine(abf.i);
Console.WriteLine(abf1.i);
}
private static void AddRef(ArgsByRef abf)
{
abf.i = 20;
Console.WriteLine(abf.i);
}
private static void ReplaceRef(ArgsByRef abf)
{
if (abf == null) throw new ArgumentNullException("abf");
var m = new ArgsByRef();
abf = m;
Console.WriteLine(abf.i);
}
}
public class ArgsByRef
{
public int i = 10;
}
现在开始来详细分析以上代码:
1. var abf = new ArgsByRef(); 语句创建一个ArgsByRef实例abf,假设abf实例指向的堆中地址为0x1323。如图:
2. var abf1 = abf; 语句创建一个ArgsByRef实例abf1,并将abf1实例的引用地址指向abf实例的引用地址,即abf1实例同样指向地址0x1323。
3.
AddRef(abf);
private static void AddRef(ArgsByRef abf)
{
abf.i = 20;
Console.WriteLine(abf.i);
}
执行AddRef方法时,传递abf实例作为参数。由于传递的是对象的引用,所以在方法内对对象的修改会直接影响对象。如图:
4. 当执行ReplaceRef方法时,传递abf1实例作为参数,创建ArgsByRef实例m,假设实例m指向的堆地址为0x1345。如图:
5. 当执行abf=m时,其实是创建了一个abf1实例引用地址的拷贝,使当前abf1实例的引用地址为0x1345。这里重点在于引用地址的拷贝,我画的表示图如下(不知道对与不对,还望大哥们多指点一下):
6. 退出ReplaceRef方法,回到主方法后,由于是引用类型的引用地址的值传递,所以并没有改变对象的引用地址。实例abf1的引用地址仍为0x1323。
所以abf1.i仍为20
最后:说了这么多,也不知道描述清楚没有,如果有错误,还希望各位看官能不吝指点。我认为重点理解引用地址的拷贝。希望对大家理解值类型和引用类型,以及按值传递和按引用传递有帮助。