如何制作数组的深层副本?

我将二维数组作为属性传递给我的用户控件。 在那里我将这些值存储在另一个二维数组中:

int[,] originalValues = this.Metrics; 

后来我改变了this.Metrics值。 但是现在如果我从originalValues中检索值,我会从this.Metrics获取更改的值。 如何制作深层副本而不只是复制C#中的引用?

您可以克隆一个数组,该数组可以复制它:

 int[,] originalValues = (int[,])this.Metrics.Clone(); 

我不知道从哪里得到这个,但这对我很有用。

 public static class GenericCopier //deep copy a list { public static T DeepCopy(object objectToCopy) { using (MemoryStream memoryStream = new MemoryStream()) { BinaryFormatter binaryFormatter = new BinaryFormatter(); binaryFormatter.Serialize(memoryStream, objectToCopy); memoryStream.Seek(0, SeekOrigin.Begin); return (T)binaryFormatter.Deserialize(memoryStream); } } } 

如果要复制的对象是数组,则可以使用:

 Array.Copy(sourceArray, destinationArray, sourceArray.Count) 

这将为您提供原始数组的单独副本到目标数组中。

问题的关键在于:

在那里我将这些值存储在另一个二维数组中

这实际上是不准确的。 你没有创建一个新的数组; 您正在将originalValues变量设置相同的数组 。 有关更详细的说明,请参见下文。


在对Pieter的回答的评论中表达的混淆是由于围绕术语“深层复制”的一些不确定性。

在复制对象时,有深度复制和浅层复制。

深度复制涉及复制属于对象的所有数据,这意味着如果对象包含本身很复杂的成员(例如,用户定义的引用类型的实例),那么这些对象也必须进行深度复制(以及他们所有的成员,等等)。

浅复制涉及简单地将所有字段从一个对象复制到另一个对象,这意味着如果对象包含引用类型,则只需要复制引用(因此复制的引用将指向相同的对象)。

对于您发布的代码:

 int[,] originalValues = this.Metrics; 

……实际上根本没有复制任何对象 。 您所做的就是复制一个引用,将this.Metrics (一个引用)的值this.Metrics变量originalValues (也是一个引用,指向同一个数组)。 这与简单的值赋值基本相同,如下所示:

 int x = y; // No objects being copied here. 

现在, Array.Clone方法实际上是一个拷贝。 但正如彼得指出的那样, 整数数组的“浅”或“深”副本之间确实没有区别,因为整数不是复杂的对象。

如果你有这样的事情:

 StringBuilder[,] builders = GetStringBuilders(); StringBuilder[,] builderCopies = (StringBuilder[,])builders.Clone(); 

…,你最终会得到一个全新的数组 (副本,是的),但是包含所有相同的StringBuilder对象(因此是一个浅的副本)。 这是深度与浅度复制发挥作用的地方; 如果您想要一个包含builders中所有StringBuilder对象副本的新数组,则需要进行深层复制。

IClonable非常棒,但除非您在顶级克隆类型中使用IClonable各种类型,否则您最终会得到参考文献AFAIK。

基于此,除非您想要遍历对象并克隆每个对象,否则这似乎是最简单的方法。

它很简单,并保证与原始深层对象的引用完全不同:

 using Newtonsoft.Json; private T DeepCopy(object input) where T : class { var copy = JsonConvert.SerializeObject((T)input); // serialise to string json object var output = JsonConvert.DeserializeObject(copy); // deserialise back to poco return output; } 

用法:

 var x = DeepCopy<{ComplexType}>(itemToBeCloned); 

其中ComplexType是想要从引用中断的任何东西。

它需要输入任何类型,对其进行字符串化,然后将其解串为新副本。

最佳使用示例:如果您因lambda查询而选择了复杂类型,并希望修改结果而不影响原始结果。

您可以使用LINQ对1d数组进行深度复制。

 var array = Enumerable.Range(0, 10).ToArray(); var array2 = array.Select(x => x).ToArray(); array2[0] = 5; Console.WriteLine(array[0]); // 0 Console.WriteLine(array2[0]); // 5 

使用2d数组,这将无法工作,因为2d数组不实现IEnumerable。

如果要深度复制引用类型数组,可以使用以下方法:

为您的类实现IClonable iterface,并将所有值类型字段的深层副本内部复制到另一个构造对象。

 class A: ICloneable { int field1; public object Clone() { A a= new A(); //copy your fields here a.field1 = this.field1; ... } } 

然后你可以使用实际的副本

 A[] array1 = new A[]{....}; A[] array2 = array1.Select(a => a.Clone()).ToList(); 

您需要创建一个新数组。 然后,您需要手动将每个元素的复制到新数组中。 您在给出的示例中所做的是创建两个引用相同数组的数组变量。

克隆方法的问题在于它是浅拷贝。 在这种情况下,因为你使用的是int ,所以它并不重要。 但是,如果你有一个类数组,那么ICLonable接口的定义就会使克隆的深度变得模糊不清。

想象一下,如果你有一个具有属性的类,这些属性具有其他类的属性。 clonable接口不会声明它是否也将克隆子成员。 此外,许多人对预期的行为有不同的看法。

因此,这就是为什么经常建议定义两个接口, IShallowCopyIDeepCopy