【问题标题】:Why ref and out in C#?为什么在 C# 中引用和输出?
【发布时间】:2009-08-22 22:04:54
【问题描述】:

使用关键字ref时,调用代码需要初始化传递的参数,但使用关键字out我们不需要这样做。

  • 我们为什么不到处使用out
  • 两者之间的确切区别是什么?
  • 请举例说明我们需要使用ref而不能使用out的情况?

【问题讨论】:

  • 我认为ref 表示inout

标签: c#


【解决方案1】:

这个MSDN article给出了答案。从那个帖子:

两种传参方式 由outref 解决的问题很巧妙 不同,但是它们都非常 常见的。之间的细微差别 这些模式导致一些非常常见的 编程错误。其中包括:

  1. 没有给out赋值 所有控制流路径中的参数
  2. 不给变量赋值 用作ref 参数

因为 C# 语言分配 不同的明确分配规则 这些不同的参数传递 模式,这些常见的编码错误是 被编译器捕获为 不正确的 C# 代码。

决定包括 refout 参数传递 模式是允许编译器 检测这些常见的编码错误 值得额外的复杂性 同时具有refout 参数 语言中的传递模式。

【讨论】:

    【解决方案2】:

    outref 的一种特殊形式,其中引用的内存不应在调用之前初始化。

    在这种情况下,C# 编译器强制在方法返回之前分配 out 变量,并且在分配之前不使用该变量。

    out 不起作用但 ref 起作用的两个示例:

    void NoOp(out int value) // value must be assigned before method returns
    {
    }
    
    void Increment(out int value) // value cannot be used before it has been assigned
    {
        value = value + 1;
    }
    

    【讨论】:

    • 此外,调用方法需要在将ref 参数传递给方法之前对其进行初始化。它可以使out 参数未初始化。在方法调用语句之后会被认为已初始化。
    【解决方案3】:

    这些答案都不让我满意,所以这是我对 refout 的看法。

    我的回答是以下两页的总结:

    1. ref (C# Reference)
    2. out (C# Reference)

    比较

    1. 方法定义和调用方法都必须显式使用ref/out关键字
    2. 这两个关键字都导致参数通过引用传递(偶数值类型
    3. 但是,当值类型通过引用传递时,没有boxing
    4. 属性不能通过outref 传递,因为属性实际上是方法
    5. ref / out 在编译时不被视为方法签名的一部分,因此如果方法之间的唯一区别是其中一个方法采用 ref 参数而另一个采用 @ 参数,则方法不能重载987654335@论据

    对比

    ref

    1. 必须先初始化才能通过
    2. 可用于向方法传递值

    out

    1. 在传递之前不必初始化
    2. 调用方法需要在方法返回前赋值
    3. 不能用于向方法传递值

    示例

    不会编译,因为方法签名的唯一区别是 ref / out

    public void Add(out int i) { }
    public void Add(ref int i) { }
    

    使用ref关键字:

    public void PrintNames(List<string> names)
    {
      int index = 0; // initialize first (#1)
    
      foreach(string name in names)
      {
        IncrementIndex(ref index);
        Console.WriteLine(index.ToString() + ". " + name);
      }
    }
    
    public void IncrementIndex(ref int index)
    {
      index++; // initial value was passed in (#2)
    }
    

    使用out关键字:

    public void PrintNames(List<string> names)
    {
      foreach(string name in names)
      {
        int index; // not initialized (#1)
    
        GetIndex(out index);
        Console.WriteLine(index.ToString() + ". " + name);
      }
    }
    
    public void GetIndex(out int index)
    {
      index = IndexHelper.GetLatestIndex(); // needs to be assigned a value (#2 & #3)
    }
    

    作者随笔

    1. 在我看来,使用out关键字的概念类似于使用ParameterDirectionOutput枚举值在ADO.NET中声明输出参数
    2. C# 中的数组是通过引用传递的,但为了使数组引用的重新分配影响调用代码中的引用,必须使用 ref 关键字

    例子:

    public void ReassignArray(ref int[] array)
    {
      array = new int[10]; // now the array in the calling code
                           // will point to this new object
    }
    

    有关引用类型与值类型的更多信息,请参阅Passing Reference-Type Parameters (C# Programming Guide)

    【讨论】:

      【解决方案4】:

      当你需要使用 ref 而不是 out 时,一个人为的例子如下:

      public void SquareThisNumber(ref int number) 
      {
         number = number * number;
      }
      
      int number = 4;
      SquareThisNumber(ref number);
      

      这里我们希望number 是一个输入输出变量,所以我们使用ref。如果我们使用了out,编译器会给出一个错误,说我们在使用它之前初始化了一个out 参数。

      【讨论】:

        【解决方案5】:

        ref 关键字允许您更改参数的值。被调用的方法可以是调用链中的一个中间环节。使用 out 关键字的方法只能在调用链的开头使用。

        另一个优点是现有值可以在方法的逻辑中使用,并且仍然保存返回值。

        在 Oracle 中,函数具有显式的 IN(默认值以及不设置方向时得到的结果)IN/OUT 和 OUT 参数。等价的是 normal(只是参数)、ref [参数] 和 out [参数]。

        【讨论】:

          【解决方案6】:

          编译器知道在调用之前不应该设置 out 变量。这允许它们在使用前被声明。但是它知道它必须在它用于返回的函数之前设置。

          【讨论】:

            【解决方案7】:

            当我们在调用以out 关键字为前缀的方法时传递值时,它会完全不同地对待它,就像我们没有将它传递给方法一样。相反,我们实际上是从方法的定义部分将 out 变量的值收集(输出)到我们调用该方法的方法 out 变量参数。

            所以out variable是方法定义中处理完成的输出,这就是为什么我们需要创建它,初始化它,只在定义中修改它。

            当我们需要从特定方法返回多个值时,使用out 变量。

            ref 变量的情况下,我们需要先初始化它,因为它的内存位置作为参数传输到方法定义。想想如果我们在通过之前不对其进行初始化会发生什么?

            【讨论】:

              猜你喜欢
              • 2021-02-27
              • 2014-09-11
              • 2011-03-31
              • 2014-09-04
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多