【问题标题】:Why is an array passed without ref, changed by CopyTo inside the method?为什么传递的数组不带ref,在方法内部被CopyTo更改?
【发布时间】:2021-11-27 09:13:31
【问题描述】:

由于数组参数是不带引用关键字传递的,所以这段代码输出数组的初始值(即1...6):

using System;
using System.Linq;

class Program 
{
    static void Main(string[] args) 
    {
        var arr = new int[] {1, 2, 3, 4, 5, 6};

        Rotate(arr, 3);
        
        Console.WriteLine(string.Join(',', arr));
    }

    static void Rotate(int[] nums, int k)
    {
        var tail = nums.TakeLast(k).ToArray();
        nums = tail.Concat(nums)
            .Take(nums.Length)
            .ToArray();
    }
}

很明显,因为在 Rotate 方法内部有它自己的值数组,从参数值复制而来。如果我想改变调用方法中的 arr 值,我需要通过 ref 将它传递给 Rotate 方法,它可以工作。 但我不明白,为什么如果用 CopyTo() 方法替换赋值,参数的行为就像是通过引用传递:

static void Rotate(int[] nums, int k)
{
    var tail = nums.TakeLast(k).ToArray();

    tail.Concat(nums)
        .Take(nums.Length)
        .ToArray()
        .CopyTo(nums, 0);
}

【问题讨论】:

  • 数组是 reference types 因此,当您将一个传递给方法时,将传递一个 reference 到数组的副本(引用本身是按值传递)。因此,方法内的数组nums 与传递给方法的引用具有相同的引用。它们是同一个数组。
  • 我明白了,但是为什么第一个代码示例输出 1...6 呢?如果我在旋转末尾添加Console.WriteLine(string.Join(',', nums));,它会打印'4,5,6,1,2,3'?此外,为什么添加 ref 也会导致 '4,5,6,1,2,3' 输出?
  • 它是自己的带有值的数组,从参数值复制 - 但您确实知道“复制”发生在调用 ToArray 的位置,而不是在调用方法的要点是什么?在 Rotate 的第一行(概念上为 {),甚至在 TakeLast(k) 执行之前,nums 作为对 arr 引用的同一内存数组的替代变量引用。此后不久,nums 被指向内存中其他位置(在第一个代码块中)的某个新数组,然后在方法结束时立即丢弃该数组

标签: c# arrays ref copyto


【解决方案1】:

这里以图形方式解释发生了什么。您的 3 种方法(regular、CopyTo 和 ref)非常不同..

我已经概述了交换算法的相对复杂性,但大多数情况下你可以忽略它。关于这一切,最重要的一点是当您使用 ref 时会发生什么,以及当您操作正在传递的引用类型的 contents 时会发生什么。内容操作与将整个内容换成其他内容非常不同

点击展开..

【讨论】:

    【解决方案2】:

    因为CopyTo() 将元素设置在数组内部,并且不会重新分配数组变量本身。

    .ToArray() 创建一个新对象,而CopyTo() 修改它。后者获得了对现有数组的引用。

    另请参阅Passing Arrays by Value and by Reference 和许多其他人。

    【讨论】:

      猜你喜欢
      • 2020-06-20
      • 1970-01-01
      • 2018-06-12
      • 1970-01-01
      • 2023-03-05
      • 2015-11-28
      • 1970-01-01
      • 1970-01-01
      • 2020-08-18
      相关资源
      最近更新 更多