如何制作数组的浅表副本?

时间:2010-10-29 16:50:42

标签: c# .net

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

int[,] originalValues = this.Metrics;

稍后,我更改this.Metrics中的值。但是现在如果我从originalValues中检索值,我会从this.Metrics获得更改的值。如何制作this.Metrics元素的副本,而不只是获取数组的引用?

9 个答案:

答案 0 :(得分:31)

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

public static class GenericCopier<T>    //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);
            }
        }
    }

答案 1 :(得分:29)

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

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

答案 2 :(得分:11)

问题的关键在于:

  

我把这个值存储在另一个二维数组中

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


Pieter's answer的评论中表达的混淆是由于围绕“深层复制”这一术语存在一些不确定性。

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

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

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

对于您发布的代码:

int[,] originalValues = this.Metrics;

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

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

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

如果你有这样的事情:

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

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

答案 3 :(得分:9)

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

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

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

答案 4 :(得分:2)

IClonable 非常棒,但除非您IClonable处于顶级克隆类型中的所有类型,否则您最终会获得引用AFAIK。

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

它非常简单,可以保证与原始深层对象的引用完全不同:

using Newtonsoft.Json;

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

用法:

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

其中ComplexType是想要中断引用的任何内容。

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

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

答案 5 :(得分:1)

您可以使用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。

答案 6 :(得分:1)

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

为您的类实现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();

答案 7 :(得分:0)

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

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

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

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

答案 8 :(得分:0)

这是一个快速解决方案,与此处的某些答案几乎相似,但其中提到了MemberwiseClone

我有只包含参考值的POCO

public class MyPoco {
    public int x { get; set; }
    public int y { get; set; }
    public int z { get; set; }

    // Add a "Clone" method.
    public MyPoco Clone() {
        return (MyPoco)this.MemberwiseClone();
    }
}

然后使用LINQ构建新的克隆数组:

var myClone = MyPocoArray.Select(x => x.Clone).ToArray();