C# 如何制作数组的浅拷贝?
我将二维数组作为属性传递给我的用户控件。我将这些值存储在另一个二维数组中:C# 如何制作数组的浅拷贝?,c#,.net,C#,.net,我将二维数组作为属性传递给我的用户控件。我将这些值存储在另一个二维数组中: int[,] originalValues = this.Metrics; 稍后,我将更改this.Metrics中的值。但是现在如果我从originalValues中检索值,我会从this.Metrics中获取更改后的值。如何复制this.Metrics的元素,而不只是获取数组的引用?您可以克隆一个数组,从而复制它: int[,] originalValues = (int[,])this.Metrics.Clone
int[,] originalValues = this.Metrics;
稍后,我将更改
this.Metrics
中的值。但是现在如果我从originalValues中检索值,我会从this.Metrics
中获取更改后的值。如何复制this.Metrics的元素,而不只是获取数组的引用?您可以克隆一个数组,从而复制它:
int[,] originalValues = (int[,])this.Metrics.Clone();
您需要创建一个新数组。然后需要手动将每个元素的值复制到新数组中。在给出的示例中,您要做的是创建两个数组变量,它们都引用相同的数组
克隆方法的问题在于它是浅拷贝。在这种情况下,因为您使用的是int
,所以不重要。然而,如果您有一个类数组,那么ICLonable接口的定义会使克隆的深度变得模糊不清
想象一下,如果你有一个类,它的属性是其他类,它的属性是其他类。clonable接口未说明是否也将克隆子成员。此外,许多人对预期的行为有不同的看法
因此,这就是为什么通常建议定义两个接口,IShallowCopy
和IDeepCopy
如果要复制的对象是数组,则可以使用:
Array.Copy(sourceArray, destinationArray, sourceArray.Count)
这将为您的目标阵列提供一个单独的原始阵列副本。我不知道这是从哪里获得的,但这对我来说很有用
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);
}
}
}
publicstatic类GenericCopier//深度复制列表
{
公共静态T DeepCopy(对象对象复制)
{
使用(MemoryStream MemoryStream=new MemoryStream())
{
BinaryFormatter BinaryFormatter=新的BinaryFormatter();
序列化(memoryStream,objectToCopy);
memoryStream.Seek(0,SeekOrigin.Begin);
返回(T)二进制格式化程序。反序列化(memoryStream);
}
}
}
问题的关键在于:
在那里,我将这些值存储在另一个二维数组中
这实际上是不准确的。您没有创建新数组;您正在将原始值
变量设置为同一数组。有关更详细的说明,请参见下文
在对的评论中表达的混乱是由于“深度复制”一词的某些不确定性造成的
在复制对象时,有深层复制和浅层复制
深度复制涉及对属于某个对象的所有数据进行复制,这意味着如果该对象包含自身复杂的成员(例如,用户定义引用类型的实例),则这些对象也必须进行深度复制(连同其所有成员,依此类推)
浅层复制只需将所有字段从一个对象复制到另一个对象,这意味着如果对象包含引用类型,则只需复制引用(因此复制的引用将指向相同的对象)
对于您发布的代码:
int[,] originalValues = this.Metrics;
。。。实际上,根本没有复制任何对象。您所做的只是复制一个引用,将this.Metrics
(一个引用)的值分配给变量originalValues
(也是一个引用,分配给同一个数组)。这与简单的赋值基本相同,如下所示:
int x = y; // No objects being copied here.
StringBuilder[,] builders = GetStringBuilders();
StringBuilder[,] builderCopies = (StringBuilder[,])builders.Clone();
现在,Array.Clone
方法实际上是一个浅拷贝。但正如Pieter所指出的,整数数组的“浅”副本和“深”副本实际上没有区别,因为整数不是复杂的对象
如果你有这样的东西:
int x = y; // No objects being copied here.
StringBuilder[,] builders = GetStringBuilders();
StringBuilder[,] builderCopies = (StringBuilder[,])builders.Clone();
…,您将得到一个全新的数组(是的,是一个副本),但其中包含所有相同的StringBuilder
对象(因此是一个浅副本)。这就是深度复制与浅复制的关系;如果您想要一个新数组,其中包含来自builders
的所有StringBuilder
对象的副本,则需要进行深度复制。您可以使用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();
IClonable很好,但除非您的顶级克隆类型中的每种类型都是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;
}
使用Newtonsoft.Json;
私有T DeepCopy(对象输入),其中T:class
{
var copy=JsonConvert.SerializeObject((T)input);//序列化为字符串json对象
var output=JsonConvert.DeserializeObject(copy);//反序列化回poco
返回输出;
}
用法:
var x = DeepCopy<{ComplexType}>(itemToBeCloned);
var x=DeepCopy(itemToBeCloned);
其中,ComplexType
是任何想要脱离引用的内容
它接受任何类型,将其字符串化,然后反字符串化为新副本
最佳使用示例:
如果您选择了一个复杂类型作为lambda查询的结果,并且希望在不影响原始结果的情况下修改结果。这里有一个快速解决方案,它几乎与这里的一些答案类似,但其中提到了
我有只包含参考值的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构建一个新阵列