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

时间:2021-01-20 21:05:14

When I make an assignment to an out or ref parameter, is the value immediately assigned to the reference provided by the caller, or are the out and ref parameter values assigned to the references when the method returns? If the method throws an exception, are the values returned?

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

For example:

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

2 个解决方案

#1


26  

Since ref and out parameters allow a method to work with the actual references that the caller passed in, all changes to those references are reflected immediately to the caller when control is returned.

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

This means in your example above (if you were to catch the ArgumentException of course), outValue and refValue would both be set to 2.

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

It is also important to note that out and ref are identical concepts at an IL level - it is only the C# compiler that enforces the extra rule for out that requires that a method set its value prior to returning. So from an CLR perspective outValue and refValue have identical semantics and are treated the same way.

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

#2


14  

Andrew is correct; I will merely add a couple of extra details.

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

First off, the correct way to think of out/ref parameters is that they are aliases for variables. That is, when you have a method M(ref int q) and call it M(ref x), q and x are two different names for exactly the same variable. A variable is a storage location; you store something in q, you're storing it in x too, because they are two different names for the same location.

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

Second, the alternative you're describing is called "copy in / copy out" referencing. In this scheme there are two storage locations and the contents of one are copied in upon the function call beginning, and copied back out when its done. As you note, the semantics of copy-in-copy-out are different than the semantics of alias references when exceptions are thrown.

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

They are also different in bizarre situations like this:

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

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

...

M(ref x, ref x);

In aliasing, x, q and r are all the same storage location, so this prints 20. In copy-in-copy-out referencing, this would print 10, and the final value of x would depend on whether the copy-out went left to right or right to left.

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

Finally, if I recall correctly, there are rare and bizarre scenarios in the implementation of expression trees where we actually implement copy-in-copy-out semantics on ref parameters. I should review that code and see if I can remember what exactly those scenarios are.

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

#1


26  

Since ref and out parameters allow a method to work with the actual references that the caller passed in, all changes to those references are reflected immediately to the caller when control is returned.

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

This means in your example above (if you were to catch the ArgumentException of course), outValue and refValue would both be set to 2.

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

It is also important to note that out and ref are identical concepts at an IL level - it is only the C# compiler that enforces the extra rule for out that requires that a method set its value prior to returning. So from an CLR perspective outValue and refValue have identical semantics and are treated the same way.

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

#2


14  

Andrew is correct; I will merely add a couple of extra details.

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

First off, the correct way to think of out/ref parameters is that they are aliases for variables. That is, when you have a method M(ref int q) and call it M(ref x), q and x are two different names for exactly the same variable. A variable is a storage location; you store something in q, you're storing it in x too, because they are two different names for the same location.

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

Second, the alternative you're describing is called "copy in / copy out" referencing. In this scheme there are two storage locations and the contents of one are copied in upon the function call beginning, and copied back out when its done. As you note, the semantics of copy-in-copy-out are different than the semantics of alias references when exceptions are thrown.

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

They are also different in bizarre situations like this:

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

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

...

M(ref x, ref x);

In aliasing, x, q and r are all the same storage location, so this prints 20. In copy-in-copy-out referencing, this would print 10, and the final value of x would depend on whether the copy-out went left to right or right to left.

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

Finally, if I recall correctly, there are rare and bizarre scenarios in the implementation of expression trees where we actually implement copy-in-copy-out semantics on ref parameters. I should review that code and see if I can remember what exactly those scenarios are.

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