6

使用reforout参数调用方法时,必须在调用方法时指定适当的关键字。从样式和代码质量的角度来看,我理解这一点(例如,这里解释过),但我很好奇在调用者中指定关键字是否还有技术需求。

例如:

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++;
}
4

4 回答 4

12

我能想到的唯一技术原因是重载解决方案:你可以

static void Increment(ref int x)

并且

static void Increment(int x)

这是允许的;如果没有ref调用,编译器将无法区分它们。

于 2013-04-19T02:47:35.747 回答
11

如果您要问是否可以设计该语言以使呼叫站点不需要这些语言,答案是肯定的。没有特别的原因他们不能被排除在外。编译器从元数据中获得了它需要的所有信息,因此它可以进行适当的转换。

也就是说,这样做会使这两个重载变得不可能:

public void DoSomething(int x);
public void DoSomething(ref int x);

编译器将无法消除歧义。

尽管refandout可以是可选的,但在这种情况下,这些重载是允许的。并且编译器可以采用默认值(即非参考),或者发出歧义错误并让您指定您真正想要的那个。

综上所述,我喜欢在通话现场指定refout。它告诉我该参数可能会被修改。在 Pascal 工作多年,其中var参数的传递方式与值参数的传递方式相同(调用站点的语法相同),我更喜欢 C# 在这方面的特殊性。

于 2013-04-19T02:47:48.333 回答
9

需要修饰符的另一个原因是将参数更改为aref破坏性更改。如果/可以被推断出来,那么针对新签名进行编译的客户端将不会检测到恶意程序员将参数从按值更改为按引用。如果客户端调用了该方法outrefout

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;
}

然后针对签名构建的客户端将不会收到编译错误,但它几乎肯定会破坏调用应用程序。

于 2013-04-19T03:13:26.320 回答
-1

编译器在构建 IL 时需要知道它正在解释的方法参数。原因之一是重载,例如:

public void (ref int x) {}
public void (int x)     {}

另一个原因是out显式允许您使用 pass-by-value 参数使用方法外的解释值。ref将提供指向参数的指针,从而将方法中的任何新值指向相同的内存位置

于 2013-04-19T03:35:43.110 回答