【问题标题】:When to pass ref keyword in何时传入 ref 关键字
【发布时间】:2011-01-12 16:17:14
【问题描述】:

我已经阅读了参数中传递和不传递 ref 之间的区别,但是,我什么时候想使用它们?

例如,我在一个方法中有一些逻辑,可以重构为它自己的方法。 Resharper 4.5 将其中一个参数设置为 ref 类型,但如果我手动进行重构,我认为我不会这样做。

显然我缺少一些理解。或许举个例子来说明当编码中的某些类型或某些场景缺少 ref 关键字时会发生什么情况?

谢谢

【问题讨论】:

  • 什么时候通过out
  • @Natrium:我发现时间是在你折腾几个到多个之后:)

标签: c#


【解决方案1】:

传递byref 仅对函数的“副作用”有意义:即,您打算修改值类型参数,或将另一个对象重新分配给给定的对象参数,并使该更改在函数调用后继续存在。示例:TryGetValue()

否则,最好坚持使用byval

【讨论】:

  • 其实TryGetValue()使用out关键字,调用者不需要初始化变量,但被调用者是。 ref 关键字用于被调用者可以更改对象引用的情况。
【解决方案2】:

从概念上讲,区别在于值类型直接存储其值,而引用类型存储对值的引用。也许您应该重新阅读一下有关引用与值类型的内容。

通过引用传递值类型(如上所示)很有用,但 ref 也可用于传递引用类型。这允许被调用的方法修改引用所引用的对象,因为引用本身是通过引用传递的。下面的示例显示,当引用类型作为 ref 参数传递时,可以更改对象本身。

   class RefRefExample
{
    static void Method(ref string s)
    {
        s = "changed";
    }
    static void Main()
    {
        string str = "original";
        Method(ref str);
        // str is now "changed"
    }
} 

MSDN

【讨论】:

    【解决方案3】:

    效果是,当控制权传回调用方法时,方法中参数的任何更改都将反映在该变量中。因此,如果您希望分配给参数的值在方法调用之后仍然存在,这是一个可能的用例

    【讨论】:

      【解决方案4】:

      让我把它分解成两个问题:

      1) 在编写方法时,何时应该使用 ref/out 形参声明?

      当您希望您的方法能够读取和写入从调用者传入的变量时,使用 ref/out,而不仅仅是读取 。 p>

      2) 为什么“提取方法”重构会产生 ref 参数?

      我不知道 Resharper 的详细信息,但我可以猜测一下。考虑以下邪恶的可变值类型:

      struct S 
      { 
        private int x;
        public int X() { return this.x; } 
        public void M() { this.x += 1; } 
      }
      

      你有一个方法:

      void Foo() 
      {
          S s = new S();
          Fred(s);
          Blah(s);
          Bar(s);
          s.M();
          Console.WriteLine(s.X()); // prints 1
      }
      

      然后你在中间位做“提取方法”:

      void NewMethod(ref S s)
      {
          Blah(s);
          Bar(s);
          s.M();
      }
      
      void Foo() 
      {
          S s = new S();
          Fred(s);
          NewMethod(ref s);
          Console.WriteLine(s.X()); // still prints 1
      }
      

      如果您创建了一个没有“ref”的方法,那么调用 NewMethod(s) 会将 s 的副本传递给 NewMethod。请记住,值类型是按值复制的;这就是为什么我们称它们为“值类型”。它将是变异的副本,然后 s.X() 返回零。重构在程序中引入语义变化是一个坏主意,重构引擎很难知道给定方法是否依赖于值类型的可变性。

      这只是您应该避免使用可变值类型的另一个原因。

      【讨论】:

        【解决方案5】:

        考虑这个例子:

        static int i = 3;
        
        public static void ChangeIntRef(ref int val)
        {
           val = 5;
        }
        
        public static void ChangeInt(int val)
        {
           val = 5;
        }
        
        Console.WriteLine(i);
        ChangeInt(i);
        Console.WriteLine(i);
        
        ChangeIntRef(ref i);
        Console.WriteLine(i);
        

        通过将参数作为ref 传递,您是在告诉编译器您真正想要的是对要传递给方法的原始变量的引用。因此,该方法可以改变原始变量的值。

        如果你从上面运行sn-p,结果是:

        3
        3
        5
        

        这应该清楚地表明,没有ref 关键字,ChangeInt 方法实际上无法更改原始值。但是,使用ref 关键字,ChangeIntRef 方法可以更改原始值。

        【讨论】:

          【解决方案6】:

          我将引用用于语义。考虑这种方法:

          void AddResultsTable(ref PlaceHolder p) // modifies p; adding a table
          {
              var t = new Table();
          
              AddTableHeader(ref t); // modifies t; adding a table header
          
              AddTableBody(ref t);  // modifies t; adding a table body
          
              AddTableFooter(ref t);  // modifies t; adding a table footer
          
              p.Controls.Add(t);
          }
          
          AddResultsTable(ref PlaceHolderResults);
          

          对比这个:

          Table ReturnTable()
          {
              var t new Table();
          
              // AddTableHeader() returns TableHeader
              t.Columns.HeaderColumns.Add(ReturnTableHeader());
          
              // ... etc.
          
              return t;
          }
          
          PlaceHolder.Controls.Add(ReturnTable());
          

          第一个 sn-p 代码对我来说看起来更干净;方法修改对象而不是返回您必须添加的新对象。这一切都保持“装箱”并隐藏在方法中。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-12-31
            • 1970-01-01
            • 1970-01-01
            • 2010-10-28
            • 2010-12-31
            • 2015-03-13
            • 2016-06-13
            • 1970-01-01
            相关资源
            最近更新 更多