C#中的“out”修饰符

4
可能是重复问题:
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;
    }

我是C#新手,正在学习out修饰符。我看到了MSDN上的这个片段

我知道out对于int原始变量很有用,但对于字符串变量,即使没有out修饰符,引用也会传递给被调用的方法,对吗?


1
不对。如果字符串参数没有标记为out,s1和s2将被视为局部变量。在处理引用本身时,请将ref和out视为引用到引用。 - Pavel Voronin
1
对于引用类型(例如字符串),引用将按值传递。这意味着您可以更改实例的内容,但不能更改引用本身。在字符串的情况下,您甚至无法更改内容,因为它们是不可变的。 - CodesInChaos
5个回答

10
引用将被传递到被调用的方法中,即使没有 out 修饰符,是吗?
是的,但没有 out 它们就不会被传回:
void M(string s1, out string s2)
{
    s1 = "one";
    s2 = "two";
}

void Main()
{
    string s = "hello", t = "world";
    // s = "hello"
    // t = "world"
    M(s, out t);
    // s = "hello"
    // t = "two"
}

string 被设计为不可变的。你可能在想 可变 的引用类型:

class Person { public string Name { get; set; } }

void Main()
{
    var p = new Person { Name = "Homer" };
    // p != null
    // p.Name = "Homer"
    M2(p);
    // p != null
    // p.Name = "Bart"
}

void M2(Person q)
{
    q.Name = "Bart";   // q references same object as p
    q = null;          // no effect, q is a copy of p
}

1
+1. 我太慢了,才想出完全相同的答案 :) - Alexei Levenkov

3

但是对于字符串变量,即使没有out修饰符,引用也会被传递到被调用的方法中,对吗?

是的,但是您无法更改引用本身。当您在方法内部设置s1 =“我已经返回了”;时,您正在将一个新的字符串实例分配给s1变量。变量本身按值传递,因此调用函数看不到此赋值。

如果您有一个类,这会更清楚:

class Foo
{
     public string Value { get; set; }
}

如果你不使用 ref 或 out 将其传递到方法中,你仍然可以看到实例内部的更改,因为它正在传递引用:
static void Method(Foo foo)
{
    foo.Value = "Bar";
}

使用这个,您可以调用:
Foo foo = new Foo { Value = "Foo" };
Method(foo);
Console.WriteLine(foo.Value); // Prints Bar

然而,如果您将值设置为不同的实例:

static void Method2(Foo foo)
{
    foo = new Foo {Value = "Bar" };
}

这不会显示出来:
Foo foo = new Foo { Value = "Foo" };
Method2(foo);
Console.WriteLine(foo.Value); // Prints Foo, not Bar!

通过传递引用或输出参数,你允许变量本身被重新赋值:

static void Method3(ref Foo foo)
{
    foo = new Foo {Value = "Bar" };
}


Foo foo = new Foo { Value = "Foo" };
Method3(ref foo); // This will change what foo references now
Console.WriteLine(foo.Value); // Prints Bar again

3

这里的区别在于:如果没有使用out,那么调用者中的值不会被更新。

static void Method(string s1, out string s2)
{
    s1 = "I've been returned";
    s2 = "changed!!!";
}

static void Main()
{
    string str1 = "one";
    string str2 "two";
    Method(str1, out str2);
    // str1 is still "one"
    // str2 is "changed!";
}

请注意,你示例中 str2null 实际上来自于 Method。由于在调用前后都是 null,因此你看不到区别。

0

这与您的主要问题有点偏题,但我认为这可能会帮助您更好地理解out修饰符的目的。

out参数的另一个有用模式可在类似于Int32.TryParse(String value, out int i)的方法中看到,它允许您编写不必手动处理常见异常的代码,例如:

int i;
if (Int32.TryParse("blah", out i))
{
  Console.WriteLine("Valid Integer");
}
else
{
  Console.WriteLine("Invalid input");
}

这只是一个简单的例子,但尝试执行某些操作并返回成功与结果值是一种相当常见且有用的模式。

.NET 中另一个更广泛使用的例子是字典上的 TryGetValue() 方法 - 请参见 Dictionary.TryGetValue Method (TKey, TValue)(MSDN)。


0

在从方法返回之前,您必须设置out参数。因此,传入的内容并不重要,因为它保证会被覆盖。

尽管作为 out 参数传递的变量在传递之前不必初始化,但调用的方法需要在方法返回之前分配一个值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接