调用带有ref
或out
参数的方法时,必须在调用方法时指定相应的关键字。我从样式和代码质量的角度理解(例如,如here所述),但我很好奇是否还需要在调用者中指定关键字。
例如:
static void Main()
{
int y = 0;
Increment(ref y); // Is there any technical reason to include ref here?
}
static void Increment(ref int x)
{
x++;
}
答案 0 :(得分:12)
我能想到的唯一技术原因是重载决议:你可能有
static void Increment(ref int x)
以及
static void Increment(int x)
This is allowed;如果调用中没有ref
,编译器将无法区分它们。
答案 1 :(得分:11)
如果你问的是语言是否可以设计成在呼叫站点不需要,那么答案是肯定的。没有特别的理由他们不能被排除在外。编译器从元数据中获取所需的所有信息,因此可以进行适当的转换。
那就是这样说,这样就不可能有这两个重载:
public void DoSomething(int x);
public void DoSomething(ref int x);
编译器无法消除歧义。
尽管ref
和out
可以是可选的,但在这种情况下,允许这些重载。并且编译器可以采用默认值(即非ref),或者发出歧义错误并让你指定你真正想要的那个。
所有这一切,我喜欢必须在呼叫站点指定ref
和out
。它告诉我可能会修改参数。在Pascal工作多年,其中var
参数的传递方式与传递值参数的方式相同(调用站点的语法相同),我更喜欢C#在这方面的特殊性。
答案 2 :(得分:9)
要求修饰符的另一个原因是将参数更改为ref
或out
更改。如果可以推断ref
/ out
,那么编译新签名的客户端将无法检测到将参数从by-value更改为by-reference的邪恶程序员。如果客户端调用方法
public int Increment(int x)
{
return x + 1;
}
使用
int result = Increment(x);
假设一个邪恶的开发人员决定更改实现,而是更改引用传递的值,并返回错误代码,如果增量导致溢出:
public int Increment(ref int x)
{
x = x + 1;
if(x == int.MinValue) // overflow
return -1;
else
return 0;
}
然后,针对签名构建的客户端将不会收到编译错误,但它几乎肯定会破坏调用应用程序。
答案 3 :(得分:-1)
编译器在构建IL时需要知道它正在解释的方法参数。 一个原因是过载如下:
public void (ref int x) {}
public void (int x) {}
另一个原因是out
将明确允许您使用pass-by-value参数使用方法之外的解释值。 ref
将提供指向参数的指针,从而将该方法中的任何新值指向相同的内存位置