【问题标题】:C# Why does the argument array has to be passed with the modifier ref if arrays are passed by reference?C# 如果数组通过引用传递,为什么必须使用修饰符 ref 传递参数数组?
【发布时间】:2020-03-26 11:00:46
【问题描述】:

我不明白为什么这个函数是这样写的:

System.Array.Resize<int>(ref int[], int)

如果数组默认通过引用传递 为什么不是这样写的:

System.Array.Resize<int>(int[], int)

【问题讨论】:

  • 他们不是。数组是引用类型,引用传递是不一样的
  • 这能回答你的问题吗? Passing Objects By Reference or Value in C#
  • 如果 Resize 方法成功,那么变量将指向不同的数组 - 调整大小的数组。这是因为使用 ref 参数您将传递对数组引用的引用,因此在方法主体中您可以修改数组引用本身。使用常规参数这是不可能的 - 对数组的引用是通过(通过复制)传递的,因此即使您在方法主体中修改它,您也会对复制的引用进行操作,而不是原始引用。

标签: c# arrays function ref modifier


【解决方案1】:

这是因为当我们将变量写入引用类型对象时,有两部分,实际的对象实例,以及变量名称所代表的引用(内部是 32 位或 64 位内存地址指针,取决于平台)。您可以通过this sharplab.io snippet 清楚地看到这一点。

当我们调用一个方法时,这个指针被复制了,但实例没有,所以:

var a = new Blah {Prop = "1"}; // Blah is a class, a reference type
Method(a);

void Method(Blah blah)
{
    blah.Prop = "2"; // changes the shared instance, we know this.

    blah = new Blah {Prop = "3"}; // has no effect.
}

Console.WriteLine(a.Prop); // 2

当我们在方法内部设置blah 时,您会看到,我们正在改变本地引用,而不是共享引用。现在如果我们使用ref 关键字:

var a = new Blah {Prop = "1"};
Method(ref a);

void Method(ref Blah blah)
{
    blah.Prop = "2"; // changes the shared instance, we know this.

    blah = new Blah {Prop = "3"}; // now changes ref a outside
}

Console.WriteLine(a.Prop); // 3!

因为参数blah是通过引用传递的,所以当我们修改它的时候,我们修改了原来的引用a

【讨论】:

    【解决方案2】:

    数组确实是引用类型,这意味着对方法内部传入的数组对象所做的更改将反映在调用方:

    public static void Foo(int[] a) {
        a[0] = 1;
    }
    
    // ...
    int[] a = new int[1];
    Foo(a);
    Console.WriteLine(a[0]); // 1
    

    但是,如果您将数组设置为方法内的其他内容:

    public static void Foo(int[] a) {
        a = null;
    }
    
    // ...
    int[] a = new int[1];
    Foo(a);
    Console.WriteLine(a[0]); // will not throw NRE
    

    将参数声明为ref 将允许重新分配参数以反映调用方。

    更改数组的大小需要创建一个新数组并因此重新分配参数。您无法通过以某种方式改变现有数组对象来调整数组大小。这就是为什么它需要声明为ref

    【讨论】:

      【解决方案3】:

      简单地说,如果您的数组作为ref 参数传递给方法,则可以将其作为一个整体替换为在该方法中创建的另一个数组。 没有ref 关键字传递的数组不是这种情况。 下面的代码说明了差异。 请注意,在这两种情况下都可以替换数组参数的各个元素(使用 of 而不使用 ref 关键字)。

      class Program
      {
          static void PrintArr(string comment, int[] arr)
          {
              Console.WriteLine($"{comment}:");
              Console.WriteLine(string.Join(", ", arr.Select(e => e.ToString())));
          }
          static void NoRef(int[] arr)
          {
              int[] localArr = { 2, 4, 8, 10 };
              arr = localArr;
          }
          static void ByRef(ref int[] arr)
          {
              int[] localArr = { 2, 4, 8, 10 };
              arr = localArr;
          }
      
          static void Main()
          {
              int[] arr;
              arr = new int[] { 1, 3, 4, 7, 9 };
      
              PrintArr("Before NoRef is called", arr);
              NoRef(arr);
              PrintArr("After NoRef is called", arr);
      
              PrintArr("Before ByRef is called", arr);
              ByRef(ref arr);
              PrintArr("After ByRef is called", arr);
      
              Console.ReadLine();
          }
      }
      

      }

      代码的输出如下所示(注意 ByRef 方法代码替换了数组。

      在调用 NoRef 之前:

      1、3、4、7、9

      NoRef 被调用后:

      1、3、4、7、9

      在调用 ByRef 之前:

      1、3、4、7、9

      在调用 ByRef 之后:

      2、4、8、10

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-03
        • 2021-05-02
        • 2011-04-05
        • 2011-12-15
        • 2020-07-30
        • 2012-04-17
        • 2019-06-29
        相关资源
        最近更新 更多