我已经阅读了MSDN上的一些教程,以便了解C#,ref
和out
中的传递,我遇到了以下代码示例:
using System;
class TheClass
{
public int x;
}
struct TheStruct
{
public int x;
}
class TestClass
{
public static void structtaker(TheStruct s)
{
s.x = 5;
}
public static void classtaker(TheClass c)
{
c.x = 5;
}
public static void Main()
{
TheStruct a = new TheStruct();
TheClass b = new TheClass();
a.x = 1;
b.x = 1;
structtaker(a);
classtaker(b);
Console.WriteLine("a.x = {0}", a.x); //prints 1
Console.WriteLine("b.x = {0}", b.x); //prints 5
}
}
教程中的注释:
此示例显示当结构传递给方法时,副本 传递结构,但是当传递类实例时,引用 通过了。
我完全理解它,但我的问题是,如果在C#中将引用传递给参数,为什么它们需要ref
,如下例所示:
void tearDown(ref myClass a)
{
a = null;
}
MyClass b = new MyClass();
this.tearDown(ref b);
assert(b == null);
//b is null
???我认为C#在C中是相同的 - 按值传递。
答案 0 :(得分:7)
在C#中,基本上所有类都是指针。但是,通过ref / out传递或不传递就像将指针传递给指针或指针本身。
当您传递一个类(根据第一个示例)时,对成员类的任何更改都会被转移。但是,更改对对象本身的引用不会产生结果。说你替换
public static void classtaker(TheClass c)
{
c.x = 5;
}
使用
public static void classtaker(TheClass c)
{
c = new TheClass();
c.x = 5;
}
由于c
不是out
或ref
参数,您可以将本地指针重新分配给c
,而不是c
的值本身。由于您只修改了本地.x
的{{1}},因此在调用此修改过的ClassTaker后,结果将是c
。
现在,根据您的第二个示例,由于a是b.x == 1
参数,因此在调用范围中将看到对值本身的更改,如示例中所示,但是从ref
中移除ref
调用会导致null
断言失败。
基本上,ref
传递可以被认为是指向指针的指针,而没有ref
/ out
的调用会将复制的指针传递给对象数据。
编辑:
可以在方法范围中指定c.X
的原因是因为对象c
指向到对象X
,并且您总是会得到无论X
/ ref
参数如何,指向out
的指针都相同。相反,ref
/ out
会修改您更改调用范围所见的值c
的能力。