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

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

例如:

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

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

这意味着在上面的示例中(如果您当然要捕获ArgumentException ), outValuerefValue都将设置为2。

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

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

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

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

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

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

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

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