问题描述:引用类型的参数加不加Ref的区别?
解答
备注:C#高级编程(第10版) 3.6.1 ref参数(92页)这一节中进行了分析(其中分析一来自于本处)。
测试代码
/// <summary>
/// 类:引用类型
/// </summary>
class A
{
public int X { get; set; }
}
class Program
{
/// <summary>
/// 参数为类类型
/// </summary>
/// <param name="a"></param>
static void ChangeA(A a)
{
a.X = 2;
a = new A { X = 3 };
}
/// <summary>
/// 参数为类类型
/// </summary>
/// <param name="a"></param>
static void ChangeA2(ref A a)
{
a.X = 2;
a = new A { X = 3 };
}
static void Main(string[] args)
{
Console.WriteLine("不带Ref修饰符");
A a = new A { X = 1 };
ChangeA(a);
Console.WriteLine($"a.x:{a.X}");
Console.WriteLine("带Ref修饰符");
A a2 = new A { X = 1 };
ChangeA2(ref a2);
Console.WriteLine($"a2.x:{a2.X}");
Console.ReadKey();
}
}
运行结果
分析一
A是类:引用类型
调用一个参数类型为类的方法,没有使用ref修饰符
当Main调用ChangeA时,
① ChangeA方法中a.X=2,改变原有对象(Main 中的 a)
② ChangeA中的下一行a=new A{X=3}在堆上创建了一个新对象,和一个新对象的引用。
③ ChangeA方法结束后,没有引用堆上的新对象,可以回收它。
④ Main中使用的变量a仍然引用值X=2的旧对象。
调用一个参数类型为类的方法,使用ref修饰符(传递对引用的引用,在C++语言中,是一个指向指针的指针)
当Main调用ChangeB时,
① ChangeB方法中a.X=2,改变原有对象(Main 中的 a)
② ChangeB中的下一行a=new A{X=3}在堆上创建了一个新对象,和一个新对象的引用。
③ Main中使用的变量a引用值X=3的新对象。
分析二
在网上找的资料:c# ref关键字对于引用类型传递的影响__wanglgkaka
在调用第一种方法时,传递了这个引用类型对象的引用副本(不是对象本身),所以对于在调用方法外部的引用和方法中的引用来说,这两个引用都指向堆上的同一个对象。所以在修改此对象的属性值时,修改会同时应用于内部和外部的两个引用上。但重新分配引用位置时,只是修改副本引用的引用位置,原引用(方法外部)的位置不变,原引用还是指向原来的对象。
如果加上Ref关键字,这时传入的参数则为引用对象的原始引用,而不是引用的副本,这样的话,你不但可以修改原始引用对象的内容,还可以再分配此引用位置(用 new 来重新实例化);
结论
- Ref修改对象地址,没有Ref只能修改对象的本身。
- 如果传递的是类,那么没有Ref是通过值传递引用类型,使用了ref就是通过引用传递引用类型。
- 你希望在方法中改变引用对象参数的应用(调用方法外的),如重新实例化对象,则需要使用Ref关键字来修饰参数。(不建议在方法内部重新实例化)