为啥传递的数组不带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更改?的主要内容,如果未能解决你的问题,请参考以下文章
React 功能组件 - 当组件返回元素数组时,如何将 refs 传递回父级?
为啥在没有 ref 的情况下将 list 传递给一个类似于通过 ref 传递的函数?