1 值传递和引用传递
值传递是传递的值的拷贝;引用传递是传递的对象本身。
测试代码:
classProgram
{
staticvoid Main(string[] args)
{
SomeValueType valueParam;
SomeRefType refParam = new SomeRefType();
//***** <1> ****
valueParam.Name = "before excute";
Console.WriteLine("值类型作参数,执行前:{0}", valueParam.Name);
Test.DoSomething(valueParam);
Console.WriteLine("值类型作参数,执行后:{0}", valueParam.Name);
Console.WriteLine("");
//***** <2> ****
refParam.Name = "before excute";
Console.WriteLine("引用类型作参数,执行前:{0}", refParam.Name);
Test.DoSomething(refParam);
Console.WriteLine("引用类型作参数,执行后:{0}", refParam.Name);
Console.WriteLine("");
//***** <3> ****
valueParam.Name = "before excute";
Console.WriteLine("值类型作参数,执行前:{0}", valueParam.Name);
Test.DoOtherSomething(valueParam);
Console.WriteLine("值类型作参数,执行后:{0}", valueParam.Name);
Console.WriteLine("");
//***** <4> ****
refParam.Name = "before excute";
Console.WriteLine("引用类型作参数,执行前:{0}", refParam.Name);
Test.DoOtherSomething(refParam);
Console.WriteLine("引用类型作参数,执行后:{0}", refParam.Name);
Console.WriteLine("");
//***** <5> ****
valueParam.Name = "before excute";
Console.WriteLine("值类型引用传递,执行前:{0}", valueParam.Name);
Test.DoSomething(ref valueParam);
Console.WriteLine("值类型引用传递,执行后:{0}", valueParam.Name);
Console.WriteLine("");
//***** <6> ****
refParam.Name = "before excute";
Console.WriteLine("引用类型引用传递,执行前:{0}", refParam.Name);
Test.DoSomething(ref refParam);
Console.WriteLine("引用类型引用传递,执行后:{0}", refParam.Name);
Console.WriteLine("");
//***** <7> ****
valueParam.Name = "before excute";
Console.WriteLine("值类型引用传递,执行前:{0}", valueParam.Name);
Test.DoOtherSomething(ref valueParam);
Console.WriteLine("值类型引用传递,执行后:{0}", valueParam.Name);
Console.WriteLine("");
//***** <8> ****
refParam.Name = "before excute";
Console.WriteLine("引用类型引用传递,执行前:{0}", refParam.Name);
Test.DoOtherSomething(ref refParam);
Console.WriteLine("引用类型引用传递,执行后:{0}", refParam.Name);
Console.WriteLine("");
Console.ReadKey();
}
}
classSomeRefType
{
publicstring Name;
}
structSomeValueType
{
publicstring Name;
}
classTest
{
publicstaticvoid DoSomething(SomeValueType v)
{
v.Name = "value type param";
Console.WriteLine("值类型作参数,执行中:"+v.Name);
}
publicstaticvoid DoSomething(SomeRefType r)
{
r.Name = "ref type param";
Console.WriteLine("引用类型作参数,执行中:" + r.Name);
}
publicstaticvoid DoOtherSomething(SomeValueType v)
{
v = new SomeValueType();
v.Name = "change value type param";
Console.WriteLine("引用类型作参数,执行中:" + v.Name);
}
publicstaticvoid DoOtherSomething(SomeRefType r)
{
r = new SomeRefType();
r.Name = "change ref type param";
Console.WriteLine("引用类型作参数,函数内创建对象,执行中:" + r.Name);
}
publicstaticvoid DoSomething(refSomeValueType v)
{
v.Name = "value type param with ref";
Console.WriteLine("引用传递值类型,执行中:" + v.Name);
}
publicstaticvoid DoSomething(refSomeRefType r)
{
r.Name = "ref type param with ref";
Console.WriteLine("引用传递引用类型,执行中:" + r.Name);
}
publicstaticvoid DoOtherSomething(refSomeValueType v)
{
v = new SomeValueType();
v.Name = "create value type param with ref ";
Console.WriteLine("引用传递值类型,函数内建对象,执行中:" + v.Name);
}
publicstaticvoid DoOtherSomething(refSomeRefType r)
{
r = new SomeRefType();
r.Name = "create ref type param with ref";
Console.WriteLine("引用传递引用类型,函数内创建对象,执行中:" + r.Name);
}
}
2 代码分析
2.1 值参数<1>
从运行后的结果来看,值类型参数在传递进函数前后没有任何变化,也就是说对形参的修改没有影响实参。
从内存分配上看,值类型参数分配在线程栈上,形参是对实参的完全拷贝。实参和形参都有各自的地址空间,两者间没有任何关联。
2.2 引用参数<2><4>
从运行后的结果来看,引用类型参数在传递进函数前后发生了变化,也就是说对形参的修改直接影响了实参的值。
从内存分配上看,引用类型参数分配在托管堆上,而保持这个对象的引用存放在线程栈上。因为实参保存的是该对象在托管堆中的地址,所以传给形参的也是这个地址,这样形参和实参都保存了相同的地址引用,他们同时指向了同一个对象地址,在函数内部如果vp不创建对象,对形参的修改会直接反映到实参上。
如果在函数内部形参rp又创建了新的对象,则内存分配如下:
从上图可以看出rp指向了另一个创建的对象,形参和实参没有了关联,此时再次对形参进行修改,修改不会影响到实参。
2.3 值类型引用传递
从运行后的结果来看,值类型参数在传递进函数前后发生了变化,也就是说对形参的修改直接影响了实参的值。
从内存分配上看,传给形参的是实参在线程堆栈上的地址,形参指向的其实仍然是实参所在的位置,两者完全同步。
2.4 引用类型引用传递<6><8>
从运行后的结果来看,引用类型参数在传递进函数前后发生了变化,也就是说对形参的修改直接影响了实参的值。
从内存分配上看,和值类型的引用传递在堆栈上的形式有一定的相似,形参保存的是实参的地址,形参并没有指向对象在托管堆上的地址,而是指向了实参,和实参共同拥有一个指向该对象内存中的引用。
如果在函数内部形参rp又创建了新的对象,则内存分配如下:
和<2><4>对照比较,在加上ref后,实参和形参自始至终都具有相同的引用。