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

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


当前回答

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

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

其他回答

可以在两个上下文中使用out上下文关键字(每个上下文都是到详细信息的链接),作为参数修饰符或在接口和委托中的泛型类型参数声明中使用。本主题讨论参数修饰符,但有关泛型类型参数声明的信息,请参阅另一个主题。

out关键字使参数通过引用传递。这类似于ref关键字,只不过ref要求在传递变量之前对其进行初始化。要使用out形参,方法定义和调用方法都必须显式使用out关键字。例如: c#

class OutExample
{
    static void Method(out int i)
    {
        i = 44;
    }
    static void Main()
    {
        int value;
        Method(out value);
        // value is now 44
    }
}

虽然作为输出参数传递的变量在传递之前不必初始化,但被调用的方法需要在方法返回之前赋值。

尽管ref和out关键字会导致不同的运行时行为,但它们在编译时不被视为方法签名的一部分。因此,如果唯一的区别是一个方法使用ref参数,而另一个方法使用out参数,则方法不能重载。例如,下面的代码将无法编译: c#

class CS0663_Example
{
    // Compiler error CS0663: "Cannot define overloaded 
    // methods that differ only on ref and out".
    public void SampleMethod(out int i) { }
    public void SampleMethod(ref int i) { }
}

然而,如果一个方法使用ref或out参数,而另一个方法两者都不使用,则可以进行重载,如下所示: c#

class OutOverloadExample
{
    public void SampleMethod(int i) { }
    public void SampleMethod(out int i) { i = 5; }
}

属性不是变量,因此不能作为参数传递。

有关传递数组的信息,请参见使用ref和out传递数组(c#编程指南)。

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

Async methods, which you define by using the async modifier.

Iterator methods, which include a yield return or yield break statement.

例子

当您希望一个方法返回多个值时,声明一个out方法非常有用。下面的示例使用out通过一个方法调用返回三个变量。注意,第三个参数被赋值为null。这使得方法可以有选择地返回值。 c#

class OutReturnExample
{
    static void Method(out int i, out string s1, out string s2)
    {
        i = 44;
        s1 = "I've been returned";
        s2 = null;
    }
    static void Main()
    {
        int value;
        string str1, str2;
        Method(out value, out str1, out str2);
        // value is now 44
        // str1 is now "I've been returned"
        // str2 is (still) null;
    }
}

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

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

如果计划读取和写入参数,则需要使用ref。如果你只打算写,你需要使用out。实际上,当您需要多个返回值时,或者当您不想使用正常的返回机制进行输出时(但这种情况应该很少发生),就会使用out。

有一些语言机制可以帮助这些用例。Ref形参在传递给方法之前必须已经初始化(强调它们是可读可写的),out形参在被赋值之前不能被读取,并且保证在方法结束时已经被写入(强调它们只能被写入)。违反这些原则将导致编译时错误。

int x;
Foo(ref x); // error: x is uninitialized

void Bar(out int x) {}  // error: x was not written to

例如,int。TryParse返回bool类型,并接受out int形参:

int value;
if (int.TryParse(numericString, out value))
{
    /* numericString was parsed into value, now do stuff */
}
else
{
    /* numericString couldn't be parsed */
}

这是需要输出两个值的一个明确示例:数值结果和转换是否成功。CLR的作者决定在这里选择out,因为他们不关心int之前可能是什么。

作为参考,你可以看联锁。增量:

int x = 4;
Interlocked.Increment(ref x);

联锁。Increment自动地增加x的值。因为需要读取x来增加它,所以在这种情况下ref更合适。你完全关心x在传递给Increment之前是什么。

在c#的下一个版本中,甚至可以声明变量In out形参,更加强调它们只输出的性质:

if (int.TryParse(numericString, out int value))
{
    // 'value' exists and was declared in the `if` statement
}
else
{
    // conversion didn't work, 'value' doesn't exist here
}

作为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语句。