什么时候C#'out'或'ref'参数的值实际返回给调用者?

Zac*_*son 14 .net c# out-parameters ref-parameters

当我对一个outref参数进行赋值时,是否会立即将值赋给调用者提供的引用,还是在方法返回时分配给引用的outref参数值?如果方法抛出异常,则返回值吗?

例如:

int callerOutValue = 1;
int callerRefValue = 1;
MyMethod(123456, out callerOutValue, ref callerRefValue);

bool MyMethod(int inValue, out int outValue, ref int refValue)
{
    outValue = 2;
    refValue = 2;

    throw new ArgumentException();

    // Is callerOutValue 1 or 2?
    // Is callerRefValue 1 or 2?
}
Run Code Online (Sandbox Code Playgroud)

And*_*are 26

由于refout参数允许方法使用调用者传入的实际引用,因此在返回控件时,对这些引用的所有更改都会立即反映给调用者.

这意味着在上面的示例中(如果您要抓住ArgumentException当然),outValue并且refValue都将设置为2.

同样重要的是要注意out并且ref在IL级别上是相同的概念 - 只有C#编译器强制执行额外规则,out这要求方法在返回之前设置其值.因此,从CLR的角度来看outValue,它refValue具有相同的语义,并以相同的方式处理.


Eri*_*ert 14

安德鲁是对的; 我只会添加一些额外的细节.

首先,考虑out/ref参数的正确方法是它们是变量的别名.也就是说,当你有一个方法M(ref int q)并将其称为M(ref x)时,q和x是完全相同变量的两个不同的名称.变量是存储位置; 你在q中存储的东西,你也存储在x中,因为它们是同一位置的两个不同的名称.

其次,您所描述的替代方案称为"复制/复制"引用.在这个方案中有两个存储位置,其中一个的内容在函数调用开始时被复制,并在完成时复制回来.正如您所注意到的,copy-in-copy-out的语义与抛出异常时别名引用的语义不同.

在这样奇怪的情况下,它们也有所不同:

void M(ref int q, ref int r)
{  
  q = 10;
  r = 20;
  print (q);
}

...

M(ref x, ref x);
Run Code Online (Sandbox Code Playgroud)

在别名中,x,q和r都是相同的存储位置,因此打印20.在copy-in-copy-out引用中,这将打印10,x的最终值将取决于复制输出是否从左到右或从右到左.

最后,如果我没记错的话,在表达式树的实现中有一些罕见且奇怪的场景,我们实际上在ref参数上实现了copy-in-copy-out语义.我应该检查一下代码,看看我是否能记住这些场景到底是什么.