如何创buildList <T>的新的深层副本(克隆)?

在下面的一段代码中,

using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; namespace clone_test_01 { public partial class MainForm : Form { public class Book { public string title = ""; public Book(string title) { this.title = title; } } public MainForm() { InitializeComponent(); List<Book> books_1 = new List<Book>(); books_1.Add( new Book("One") ); books_1.Add( new Book("Two") ); books_1.Add( new Book("Three") ); books_1.Add( new Book("Four") ); List<Book> books_2 = new List<Book>(books_1); books_2[0].title = "Five"; books_2[1].title = "Six"; textBox1.Text = books_1[0].title; textBox2.Text = books_1[1].title; } } } 

我使用一个Book对象types来创build一个List<T>并用一些项目给它们填充一个唯一的标题(从“一”到“五”)。

然后我创buildList<Book> books_2 = new List<Book>(books_1)

从这一点来说,我知道这是一个列表对象的克隆,但book_2中的book对象仍然是book_2中book对象的books_1 。 通过对books_2的两个第一个元素进行books_2 ,然后在TextBox检查book_1相同元素, book_1了这一点。

books_1[0].title and books_2[1].title确实已经改变为books_2[0].title and books_2[1].title的新值。

现在问题

我们如何创buildList<T>的新硬拷贝? 这个想法是books_1books_2完全相互独立。

我感到很失望,微软并没有提供像Ruby一样使用clone()方法的简洁快捷的解决scheme。

佣工真的很棒,就是使用我的代码,并用一个可行的解决scheme来改变它,这样可以编译和工作。 我认为这将真正帮助新手理解为这个问题提供的解决scheme。

编辑:请注意Book类可能会更复杂,有更多的属性。 我试图保持简单。

您需要创build新的Book对象,然后将它们放在一个新的List

 List<Book> books_2 = books_1.Select(book => new Book(book.title)).ToList(); 

更新:稍微简单… List<T>有一个名为ConvertAll的方法,返回一个新的列表:

 List<Book> books_2 = books_1.ConvertAll(book => new Book(book.title)); 

创build一个在Book类中实现的通用ICloneable<T>接口,以便该类知道如何创build自己的副本。

 public interface ICloneable<T> { T Clone(); } public class Book : ICloneable<Book> { public Book Clone() { return new Book { /* set properties */ }; } } 

然后,您可以使用Mark提到的linq或ConvertAll方法。

 List<Book> books_2 = books_1.Select(book => book.Clone()).ToList(); 

要么

 List<Book> books_2 = books_1.ConvertAll(book => book.Clone()); 

我感到很失望,微软并没有提供像Ruby一样使用clone()方法的简洁快捷的解决scheme。

除了创build深层复制,它会创build一个浅层副本。

通过深度复制,您必须小心谨慎,您要复制到底是什么。 可能的问题的一些例子是:

  1. 在对象图中循环。 例如, Book有一个AuthorAuthor有他的Book的列表。
  2. 引用一些外部对象。 例如,一个对象可能包含写入文件的开放Stream
  3. 活动。 如果一个对象包含一个事件,几乎任何人都可以订阅它。 如果用户像GUI Window一样,这可能会特别成问题。

现在,基本上有两种如何克隆的方法:

  1. 在你需要克隆的每个类中实现一个Clone()方法。 (也有ICloneable接口,但是你不应该使用它;使用习惯ICloneable<T>接口作为Trevorbuild议是好的。)如果你知道你需要的是创build这个类的每个字段的浅拷贝,可以使用MemberwiseClone()来实现它。 作为替代,您可以创build一个“复制构造函数”: public Book(Book original)
  2. 使用序列化将对象序列化到MemoryStream ,然后反序列化它们。 这要求您将每个类标记为[Serializable] ,并且还可以configuration序列化的方式(以及如何)。 但是这更多的是一个“快速和肮脏的”解决scheme,而且很可能性能也不高。

好,

如果您将所有涉及的类标记为可序列化,则可以:

 public static List<T> CloneList<T>(List<T> oldList) { BinaryFormatter formatter = new BinaryFormatter(); MemoryStream stream = new MemoryStream(); formatter.Serialize(stream, oldList); stream.Position = 0; return (List<T>)formatter.Deserialize(stream); } 

资源:

https://social.msdn.microsoft.com/Forums/en-US/5c9b4c31-850d-41c4-8ea3-fae734b348c4/copy-listsomeobject-to-clone-list?forum=csharpgeneral

如果Array类满足您的需要,您也可以使用List.ToArray方法,它将元素复制到一个新的数组中。

参考: http : //msdn.microsoft.com/en-us/library/x303t819(v=vs.110).aspx

由于Clone会返回Book的一个对象实例,因此该对象首先需要转换为Book,然后才能调用ToList 。 上面的例子需要写成:

 List<Book> books_2 = books_1.Select(book => (Book)book.Clone()).ToList();