我将一个二维数组作为属性传递给了我的用户控件。在那里,我将这些值存储在另一个二维数组中:
int[,] originalValues = this.Metrics;
后来,我更改了this.Metrics
中的值。但现在,如果我从originalValues
检索值,我会得到来自this.Metrics
的更改后的值。如何复制this.Metrics
的元素而不仅仅是获取数组的引用?
我不知道这是从哪里得来的,但对我很有效。
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);
}
}
}
您可以克隆一个数组,这将复制它:
int[,] originalValues = (int[,])this.Metrics.Clone();
Clone()
就足够了。 - Pieter van Ginkel你的问题核心在这里:
然后我将这些值存储在另一个二维数组中
事实上这是不准确的。你没有创建一个新的数组;你是将你的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();
int x = y;
确实复制了 某些 东西;int 是不可变的,将其分配给一个新变量将始终创建一个副本。但是,是的,在这里基本上发生的就是这样,只是复制的值是数组的 _引用_,而不是数组本身。 - Nyerguds如果您复制的对象是数组,则可以使用以下方法:
Array.Copy(sourceArray, destinationArray, sourceArray.Count)
Array.Copy(sourceArray, destinationArray, sourceArray.Count())
或者 Array.Copy(sourceArray, destinationArray, sourceArray.Length)
吗? - FranzHuber23IClonable很好,但除非您在顶级克隆类型中IClonable
每个类型,否则您最终会得到引用(据我所知)。
基于此,除非您想遍历对象并克隆其中的每个对象,否则这似乎是最简单的方法。
它很简单,并保证了与原始深层对象的引用之间的干净断裂:
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查询的结果,并希望修改结果而不影响原始数据。
IClonable
接口,并在内部将所有值类型字段进行深度复制到另一个构造对象中。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();
你可以使用LINQ深度复制一个1维数组。
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
使用二维数组将无法实现此操作,因为二维数组没有实现IEnumerable接口。
这里有一个快速解决方案,与某些答案几乎相似,但是提到了 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();
你需要创建一个新数组。然后,你需要手动将每个元素的值复制到新数组中。在给定的示例中,你正在创建两个引用同一数组的数组变量。
clone方法的问题在于它是浅拷贝。在这种情况下,因为你正在使用int
,所以无所谓。但是,如果你有一个类的数组,ICLonable接口的定义对克隆将会进行多深是不明确的。
想象一下,如果你有一个包含其他类属性的类,而这些类又有其他类属性。clonable接口没有说明它是否也会克隆子成员。而且,许多人对期望行为有不同的看法。
因此,通常建议定义两个接口,IShallowCopy
和IDeepCopy
。
Array.Clone()
"创建一个 浅复制 数组"(强调添加)。 - jmbpiano