为啥传递的数组不带ref,在方法内部被CopyTo更改?

Posted

技术标签:

【中文标题】为啥传递的数组不带ref,在方法内部被CopyTo更改?【英文标题】: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 被指向内存中其他位置(在第一个代码块中)的某个新数组,然后在方法结束时立即丢弃该数组 【参考方案1】:

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

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

点击展开..

【讨论】:

【参考方案2】:

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

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

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

【讨论】:

以上是关于为啥传递的数组不带ref,在方法内部被CopyTo更改?的主要内容,如果未能解决你的问题,请参考以下文章

ref和out实际用法

React 功能组件 - 当组件返回元素数组时,如何将 refs 传递回父级?

在 C# 中传递数组参数:为啥它是通过引用隐式传递的?

为啥在没有 ref 的情况下将 list 传递给一个类似于通过 ref 传递的函数?

如果我们可以简单地在 props 中传递创建的 ref,为啥还需要 React forwardRef? [复制]

为啥 std::strcpy 仍然适用于 char copyTo[0] 之类的目的地?