前几天有人问我他们什么时候应该使用参数关键字out而不是ref。虽然我(我认为)理解ref和out关键字之间的区别(之前已经问过了),最好的解释似乎是ref == in和out,有什么(假设或代码)的例子,我应该总是使用out而不是ref。

既然ref更通用,为什么还要用out呢?它只是语法上的糖吗?


当前回答

在c#中如何使用in或out或ref ?

All keywords in C# have the same functionality but with some boundaries. in arguments cannot be modified by the called method. ref arguments may be modified. ref must be initialized before being used by caller it can be read and updated in the method. out arguments must be modified by the caller. out arguments must be initialized in the method Variables passed as in arguments must be initialized before being passed in a method call. However, the called method may not assign a value or modify the argument.

以下类型的方法不能使用in、ref和out关键字:

Async方法,通过使用Async修饰符来定义。 迭代器方法,其中包括yield return或yield break语句。

其他回答

使用out表示该参数未被使用,仅被设置。这有助于调用者理解您总是在初始化参数。

此外,ref和out不仅适用于值类型。它们还允许您重置引用类型从方法中引用的对象。

基本上ref和out都用于在方法之间传递对象/值

out关键字使参数通过引用传递。这类似于ref关键字,只不过ref要求在传递变量之前对其进行初始化。

out:参数没有初始化,必须在方法中初始化

ref:参数已经初始化,可以在方法中读取和更新。

引用类型的“ref”有什么用?

您可以将给定的引用更改为不同的实例。

你知道吗?

Although the ref and out keywords cause different run-time behavior, they are not considered part of the method signature at compile time. Therefore, methods cannot be overloaded if the only difference is that one method takes a ref argument and the other takes an out argument. You can't use the ref and out keywords for the following kinds of methods: Async methods, which you define by using the async modifier. Iterator methods, which include a yield return or yield break statement. Properties are not variables and therefore cannot be passed as out parameters.

你是正确的,在语义上,ref同时提供“in”和“out”功能,而out只提供“out”功能。有一些事情需要考虑:

out requires that the method accepting the parameter MUST, at some point before returning, assign a value to the variable. You find this pattern in some of the key/value data storage classes like Dictionary<K,V>, where you have functions like TryGetValue. This function takes an out parameter that holds what the value will be if retrieved. It wouldn't make sense for the caller to pass a value into this function, so out is used to guarantee that some value will be in the variable after the call, even if it isn't "real" data (in the case of TryGetValue where the key isn't present). out and ref parameters are marshaled differently when dealing with interop code

Also, as an aside, it's important to note that while reference types and value types differ in the nature of their value, every variable in your application points to a location of memory that holds a value, even for reference types. It just happens that, with reference types, the value contained in that location of memory is another memory location. When you pass values to a function (or do any other variable assignment), the value of that variable is copied into the other variable. For value types, that means that the entire content of the type is copied. For reference types, that means that the memory location is copied. Either way, it does create a copy of the data contained in the variable. The only real relevance that this holds deals with assignment semantics; when assigning a variable or passing by value (the default), when a new assignment is made to the original (or new) variable, it does not affect the other variable. In the case of reference types, yes, changes made to the instance are available on both sides, but that's because the actual variable is just a pointer to another memory location; the content of the variable--the memory location--didn't actually change.

与ref关键字一起传递表示原始变量和函数参数实际上都指向相同的内存位置。同样,这只影响赋值语义。如果将一个新值赋给其中一个变量,那么由于其他变量指向相同的内存位置,新值将反映在另一侧。

需要注意的是,in在c# ver 7.2是一个有效的关键字:

The in parameter modifier is available in C# 7.2 and later. Previous versions generate compiler error CS8107 ("Feature 'readonly references' is not available in C# 7.0. Please use language version 7.2 or greater.") To configure the compiler language version, see Select the C# language version. ... The in keyword causes arguments to be passed by reference. It makes the formal parameter an alias for the argument, which must be a variable. In other words, any operation on the parameter is made on the argument. It is like the ref or out keywords, except that in arguments cannot be modified by the called method. Whereas ref arguments may be modified, out arguments must be modified by the called method, and those modifications are observable in the calling context.

它取决于编译上下文(参见下面的示例)。

out和ref都表示通过引用传递变量,但ref要求变量在传递之前进行初始化,这在封送处理上下文中是一个重要的区别(Interop: UmanagedToManagedTransition或反之亦然)。

MSDN警告说:

不要将按引用传递的概念与引用类型的概念混淆。这两个概念是不一样的。不管方法形参是值类型还是引用类型,都可以通过ref来修改。通过引用传递值类型时,没有对值类型进行装箱。

来自MSDN官方文档:

:

out关键字使参数通过引用传递。这类似于ref关键字,不同的是ref要求在传递变量之前对变量进行初始化

裁判:

ref关键字使参数按引用传递,而不是按值传递。通过引用传递的效果是,对方法中参数的任何更改都反映在调用方法中的底层参数变量中。引用形参的值始终与基础实参变量的值相同。

当参数被赋值时,我们可以验证out和ref确实是相同的:

CIL的例子:

考虑下面的例子

static class outRefTest{
    public static int myfunc(int x){x=0; return x; }
    public static void myfuncOut(out int x){x=0;}
    public static void myfuncRef(ref int x){x=0;}
    public static void myfuncRefEmpty(ref int x){}
    // Define other methods and classes here
}

在CIL中,myfuncOut和myfuncRef的指令与预期相同。

outRefTest.myfunc:
IL_0000:  nop         
IL_0001:  ldc.i4.0    
IL_0002:  starg.s     00 
IL_0004:  ldarg.0     
IL_0005:  stloc.0     
IL_0006:  br.s        IL_0008
IL_0008:  ldloc.0     
IL_0009:  ret         

outRefTest.myfuncOut:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldc.i4.0    
IL_0003:  stind.i4    
IL_0004:  ret         

outRefTest.myfuncRef:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldc.i4.0    
IL_0003:  stind.i4    
IL_0004:  ret         

outRefTest.myfuncRefEmpty:
IL_0000:  nop         
IL_0001:  ret         

Nop:不操作,ldloc: load local, stloc: stack local, ldarg: load参数,bs。S:分支到目标....

(见:CIL指令列表)