我将二维数组作为属性传递给我的用户控件。在那里,我将这些值存储在另一个二维数组中:
int[,] originalValues = this.Metrics;
稍后,我更改this.Metrics
中的值。但是现在如果我从originalValues中检索值,我会从this.Metrics
获得更改的值。如何制作this.Metrics
元素的副本,而不只是获取数组的引用?
答案 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接口不会声明它是否也将克隆子成员。此外,许多人对预期的行为有不同的看法。
因此,这就是为什么经常建议定义两个接口IShallowCopy
和IDeepCopy
。
答案 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();