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

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


当前回答

只是在OP的注释中澄清一下,对ref和out的使用是“对在方法外部声明的值类型或结构的引用”,这已经在incorrect中建立了。

考虑在StringBuilder上使用ref,它是一种引用类型:

private void Nullify(StringBuilder sb, string message)
{
    sb.Append(message);
    sb = null;
}

// -- snip --

StringBuilder sb = new StringBuilder();
string message = "Hi Guy";
Nullify(sb, message);
System.Console.WriteLine(sb.ToString());

// Output
// Hi Guy

与此相反:

private void Nullify(ref StringBuilder sb, string message)
{
    sb.Append(message);
    sb = null;
}

// -- snip --

StringBuilder sb = new StringBuilder();
string message = "Hi Guy";
Nullify(ref sb, message);
System.Console.WriteLine(sb.ToString());

// Output
// NullReferenceException

其他回答

需要注意的是,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上下文关键字(每个上下文都是到详细信息的链接),作为参数修饰符或在接口和委托中的泛型类型参数声明中使用。本主题讨论参数修饰符,但有关泛型类型参数声明的信息,请参阅另一个主题。

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

作为ref传递的参数必须在传递给方法之前初始化,而out形参在传递给方法之前不需要初始化。

你应该使用out,除非你需要参考。

当数据需要被编组到另一个进程时,它会产生很大的不同,这可能会花费很高。因此,您希望避免在方法不使用初始值时编组初始值。

除此之外,它还向声明或调用的读者显示初始值是相关的(并可能被保留),还是被丢弃。

作为一个微小的区别,out形参不需要初始化。

out的示例:

string a, b;
person.GetBothNames(out a, out b);

其中GetBothNames是一个原子检索两个值的方法,无论a和b是什么,该方法都不会改变行为。如果调用到夏威夷的服务器,将初始值从这里复制到夏威夷是浪费带宽。使用ref的类似代码段:

string a = String.Empty, b = String.Empty;
person.GetBothNames(ref a, ref b);

可能会让读者感到困惑,因为看起来a和b的初始值是相关的(尽管方法名表明它们不是)。

引用的例子:

string name = textbox.Text;
bool didModify = validator.SuggestValidName(ref name);

这里的初始值与方法相关。

Out是ref的约束版本。

在方法体中,需要在离开方法之前为所有out参数赋值。 另外,分配给out形参的值将被忽略,而ref要求分配这些值。

out允许你这样做:

int a, b, c = foo(out a, out b);

其中ref需要分配a和b。