List <T>的深层副本

我试图做一个generics列表的深层副本,并想知道是否有任何其他的方式,然后创build复制方法,并实际上每个成员一次复制。 我有一个看起来有点像这样的类:

public class Data { private string comment; public string Comment { get { return comment; } set { comment = value; } } private List<double> traceData; public List<double> TraceData { get { return traceData; } set { traceData = value; } } } 

我有一个列表上面的数据,即List<Data> 。 我想要做的是将List的子集的跟踪数据绘制到一个图上,可能会进行一些缩放或扫描数据。 我显然不需要在列表中绘制所有内容,因为它们不适合屏幕。

我最初尝试使用List.GetRange()方法获取列表的子集,但似乎下面的List<double>被浅拷贝而不是深拷贝。 当我再次使用List.GetRange()获取子集时,我得到了先前修改的数据,而不是其他地方检索到的原始数据。

任何人都可以给我一个关于如何解决这个问题的方向? 非常感谢。

在C#中使用这种方法的习惯方法是在Data上实现ICloneable ,然后编写一个Clone方法来执行深层复制(然后假定Enumerable.CloneRange方法可以一次性克隆部分列表)。任何内置的技巧或框架方法,使之更容易。

除非内存和性能是一个真正的问题,否则我build议你尽量重新devise它来操作不可变的Data对象。 它会更简单。

你可以试试这个

  public static object DeepCopy(object obj) { if (obj == null) return null; Type type = obj.GetType(); if (type.IsValueType || type == typeof(string)) { return obj; } else if (type.IsArray) { Type elementType = Type.GetType( type.FullName.Replace("[]", string.Empty)); var array = obj as Array; Array copied = Array.CreateInstance(elementType, array.Length); for (int i = 0; i < array.Length; i++) { copied.SetValue(DeepCopy(array.GetValue(i)), i); } return Convert.ChangeType(copied, obj.GetType()); } else if (type.IsClass) { object toret = Activator.CreateInstance(obj.GetType()); FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (FieldInfo field in fields) { object fieldValue = field.GetValue(obj); if (fieldValue == null) continue; field.SetValue(toret, DeepCopy(fieldValue)); } return toret; } else throw new ArgumentException("Unknown type"); } 

感谢代码项目的DetoX83 文章 。

如果IClonable的方式对你来说太棘手了。 我build议转换到某种东西,然后回来。 它可以用BinaryFormatter或像Servicestack.Text这样的Json转换器来完成,因为它是.Net中速度最快的一个。

代码应该是这样的:

 MyClass mc = new MyClass(); string json = mc.ToJson(); MyClass mcCloned = json.FromJson<MyClass>(); 

mcCloned不会引用mc。

最简单(但很脏)的方法是由你的类实现ICloneable ,并使用下一个扩展方法:

 public static IEnumerable<T> Clone<T>(this IEnumerable<T> collection) where T : ICloneable { return collection.Select(item => (T)item.Clone()); } 

用法:

 var list = new List<Data> { new Data { Comment = "comment", TraceData = new List { 1, 2, 3 } }; var newList = list.Clone(); 

你可以做的另一件事是将你的类标记为可serializable并使用二进制序列化。 这是一个工作的例子

  public class Program { [Serializable] public class Test { public int Id { get; set; } public Test() { } } public static void Main() { //create a list of 10 Test objects with Id's 0-10 List<Test> firstList = Enumerable.Range(0,10).Select( x => new Test { Id = x } ).ToList(); using (var stream = new System.IO.MemoryStream()) { var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); binaryFormatter.Serialize(stream, firstList); //serialize to stream stream.Position = 0; //deserialize from stream. List<Test> secondList = binaryFormatter.Deserialize(stream) as List<Test>; } Console.ReadKey(); } } 

如果你让你的对象不变,你不需要担心传递它们的副本,那么你可以做这样的事情:

 var toPlot = list.Where(d => d.ShouldBePlotted()); 

由于您的集合是可变的,因此您需要以编程方式实现深度复制:

 public class Data { public string Comment { get; set; } public List<double> TraceData { get; set; } public Data DeepCopy() { return new Data { Comment = this.Comment, TraceData = this.TraceData != null ? new List<double>(this.TraceData) : null; } } } 

Comment字段可以被浅拷贝,因为它已经是一个不可变的类。 您需要为TraceData创build一个新的列表,但是这些元素本身是不可变的,不需要特殊处理来复制它们。

当我再次使用List.GetRange()获取子集时,我得到了先前修改的数据,而不是其他地方检索到的原始数据。

使用你的新的DeepCopy方法:

 var pointsInRange = dataPoints .Select(x => x.DeepCopy()) .GetRange(start, length); 
 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DeepListCopy_testingSome { class Program { static void Main(string[] args) { List<int> list1 = new List<int>(); List<int> list2 = new List<int>(); //populate list1 for (int i = 0; i < 20; i++) { list1.Add(1); } /////// Console.WriteLine("\n int in each list1 element is:\n"); /////// foreach (int i in list1) { Console.WriteLine(" list1 elements: {0}", i); list2.Add(1); } /////// Console.WriteLine("\n int in each list2 element is:\n"); /////// foreach (int i in list2) { Console.WriteLine(" list2 elements: {0}", i); } ///////enter code here for (int i = 0; i < list2.Count; i++) { list2[i] = 2; } /////// Console.WriteLine("\n Printing list1 and list2 respectively to show\n" + " there is two independent lists,ie, two differens" + "\n memory locations after modifying list2\n\n"); foreach (int i in list1) { Console.WriteLine(" Printing list1 elements: {0}", i); } /////// Console.WriteLine("\n\n"); /////// foreach (int i in list2) { Console.WriteLine(" Printing list2 elements: {0}", i); } Console.ReadKey(); }//end of Static void Main }//end of class } 

深入序列化一个对象的一种快捷通用的方法是使用JSON.net 。 以下扩展方法允许序列化任意任意对象的列表,但能够跳过entity framework导航属性,因为这些可能导致循环依赖和不需要的数据提取。

方法

 public static List<T> DeepClone<T>(this IList<T> list, bool ignoreVirtualProps = false) { JsonSerializerSettings settings = new JsonSerializerSettings(); if (ignoreVirtualProps) { settings.ContractResolver = new IgnoreNavigationPropsResolver(); settings.PreserveReferencesHandling = PreserveReferencesHandling.None; settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; settings.Formatting = Formatting.Indented; } var serialized = JsonConvert.SerializeObject(list, settings); return JsonConvert.DeserializeObject<List<T>>(serialized); } 

用法

 var clonedList = list.DeepClone(); 

默认情况下,JSON.NET只序列化公共属性。 如果私有财产也必须被克隆, 这个解决scheme可以被使用。

这种方法允许快速(de)序列化 复杂的对象层次结构 。