简体   繁体   English

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

[英]When is the value of a C# 'out' or 'ref' parameter actually returned to the caller?

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? 当我对outref参数进行赋值时,是否会立即将值赋给调用者提供的引用,还是在方法返回时分配给引用的outref参数值? If the method throws an exception, are the values returned? 如果方法抛出异常,则返回值吗?

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

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

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 ), outValuerefValue都将设置为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. 同样重要的是要注意outref是IL级别的相同概念 - 只有C#编译器强制执行out的额外规则,这要求方法在返回之前设置其值。 So from an CLR perspective outValue and refValue have identical semantics and are treated the same way. 因此,从CLR的角度来看, outValuerefValue具有相同的语义并且以相同的方式处理。

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 . 首先,考虑out / ref参数的正确方法是它们是变量的别名 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 . 也就是说,当你有一个方法M(ref int q)并将其称为M(ref x)时,q和x是完全相同变量的两个不同的名称 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. 你在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. 最后,如果我没记错的话,在表达式树的实现中有一些罕见且奇怪的场景,我们实际上在ref参数上实现了copy-in-copy-out语义。 I should review that code and see if I can remember what exactly those scenarios are. 我应该检查一下代码,看看我是否能记住这些场景到底是什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM