【问题标题】:'out' modifier in C#C#中的'out'修饰符
【发布时间】:2012-08-26 15:31:33
【问题描述】:

可能重复:
C# - Reference type still needs pass by ref?

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# 新手,正在学习修饰符。我遇到了this snippet on MSDN

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

【问题讨论】:

  • 不,不对。如果字符串参数未标记为 out,则 s1 和 s2 将被视为局部变量。考虑 ref 和 out,因为您不是使用引用本身,而是使用对引用的引用。
  • 对于引用类型(例如字符串),引用将按值传递。这意味着您可以更改实例的内容,但不能更改引用本身。对于字符串,您甚至无法更改内容,因为它们是不可变的。

标签: c# out


【解决方案1】:

这对于您的主要问题来说有点离题,但我认为这可能有助于您更好地理解 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)。

【讨论】:

    【解决方案2】:

    这里有区别:如果不是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。

    【讨论】:

      【解决方案3】:

      即使没有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。我很想得出完全相同的答案:)
      【解决方案4】:

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

      是的,但您不能更改引用本身。当您在方法内设置s1 = "I've been returned"; 时,您将一个新的字符串实例分配给 s1 变量。变量本身是按值传递的,所以调用函数看不到这个赋值。

      如果你有一个类,这就更清楚了:

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

      如果你将它传递给一个没有 ref 或 out 的方法,你仍然可以看到实例的 inside 变化,因为它正在传递引用:

      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!
      

      通过 ref 或 out 传递,您可以重新分配变量本身:

      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
      

      【讨论】:

        【解决方案5】:

        需要在从方法返回之前设置out parameters。所以传入什么都没关系,因为它保证会被覆盖。

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

        【讨论】:

          猜你喜欢
          • 2022-06-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-04-11
          • 1970-01-01
          • 1970-01-01
          • 2011-03-19
          相关资源
          最近更新 更多