如何创建列表集的深层副本

假设我有以下课程:

public class Author { public int ID {get; private set;} public string firstName {get; private set;} public string lastName {get; private set; } public Author(int id, string firstname, string lastname) { this.ID = ID; this.firstName = firstname; this.lastName = lastName; } public static Author Clone(Author clone) { Author copyAuth = new Author(clone.ID, clone.firstName, clone.lastName); return copyAuth; } } 

 public class Book { public string bookTitle {get; private set;} private List authors; private List copyofAuthors; public string ISBN {get; private set; } public Book(string bookTitle, List authors, string ISBN) { copyofAuthors = new List(); this.bookTitle = bookTitle; this.ISBN = ISBN; //How do I create a deep copy of my authors List? foreach(Author copy in authors) { Author addAuthors = Author.Clone(copy); copyofAuthors.Add(addAuthors); } } } 

如何创建List集合的深层副本? 我已经阅读了StackOverFlow上的其他页面,这些页面提示了序列化,以及我不熟悉并且看起来令人困惑的建议。

我按照这个链接创建了我的克隆方法。

问题1:

以上实施是否被视为深层复制? 如果是这样,可以这样做吗? 我的意思是,构造函数中的foreach循环将作者复制到新的列表集合。

问题2:

如果我修改了copyofAuthor集合中的任何内容,它就不再引用原始集合了吗? 那么原始系列应该保持不变?

更新#1:

  public List Authors { get { return returnAuthors(authors); } } private List returnAuthors(List copyList) { List getAuthors = new List(); foreach(Author copy in copyList){ getAuthors.Add(Author.Clone(copy)); } return getAuthors; } 

我是否正确实现了我的getter集合,以便在返回List集合时, 它独立于原始集合 ? 因此,从getter返回的集合所做的任何更改都不会反映在原始集合中吗?

更新#2:

使用ReadOnlyCollection

  public class Book { public string bookTitle {get; private set;} private ReadOnlyCollection authors; public string ISBN {get; private set; } public Book(string bookTitle, ReadOnlyCollection authors, string ISBN) { this.bookTitle = bookTitle; this.ISBN = ISBN; //Is it okay to do this? this.authors = authors; } public List Authors { get { //Create a shallow copy return new ReadOnlyCollection(authors); } } } 

在评论中对此进行排序太难了。

  • 从Author类中删除Clone方法。 这没用。

在你的书课上,你有两个问题需要解决。

  • 构造函数采用作者列表,但传递它的调用者可能会更改它。 如果我们只是复制引用,那么调用者可能会意外地更改该书所持有的列表。

  • 这本书给出了一份作者名单。 如果呼叫者再次向该列表添加内容,他们就会改变该书。

您可以使用不可变集合解决这两个问题。 如果您还没有使用NuGet,请下载不可变集合库。

 using System.Collections.Immutable; ... public class Book { public string bookTitle {get; private set;} private ImmutableList authors; public IReadOnlyList Authors { get { return authors; } } public string ISBN {get; private set; } public Book(string bookTitle, IEnumerable authors, string ISBN) { this.authors = ImmutableList.Empty.AddRange(authors); this.bookTitle = bookTitle; this.ISBN = ISBN; } } 

那里。 现在你复制了作者序列 ,所以如果调用者改变了那个序列,不用担心,你有副本。 然后你分发一个由不可变集合实现的IReadOnlyList,所以没有人可以改变它。

结合更多的事情。 你问“这是对的吗?”

 public class Book { private ReadOnlyCollection authors; public Book(ReadOnlyCollection authors) { //Is it okay to do this? this.authors = authors; } public List Authors { get { //Create a shallow copy return new ReadOnlyCollection(authors); } } 

(删除了多余的东西)。

不,这有点不对,原因有两个。 首先,只读集合只是一个可变集合的包装器 。 您仍然处于调用者控制底层集合的情况,因此可以更改它。

其次,打字不太合适; 你不能将ReadOnlyCollection转换为List。

我知道这令人困惑。 这里有一个微妙的区别。 只读集合就是: 只能阅读它。 这并不意味着别人不能写出来! 这样的集合仍然是可变的 ,它不是可变 。 不可变的集合是真正不可改变的; 没人能改变它。

接下来:通过使作者和书籍都不可变,你做得很好。 但是如果你想改变呢? 如您所知,更改不可变书籍意味着制作新书。 但是你已经有了一本旧书; 你怎么能有效地做到这一点? 常见的模式是:

 public class Book { public string Title {get; private set;} private ImmutableList authors; public IReadOnlyList Authors { get { return authors; } } public string ISBN {get; private set; } public Book(string title, IEnumerable authors, string ISBN) : this( title, ImmutableList.Empty.AddRange(authors), ISBN) {} public Book(string title, ImmutableList authors, string ISBN) { this.Title = title; this.Authors = authors; this.ISBN = ISBN; } public Book WithTitle(string newTitle) { return new Book(newTitle, authors, ISBN); } public Book WithISBN(string newISBN) { return new Book(Title, authors, newISBN); } public Book WithAuthor(Author author) { return new Book(Title, authors.Add(author), ISBN); } public static readonly Empty = new Book("", ImmutableList.Empty, ""); } 

现在你可以这样做:

 Book tlotr = Book.Empty.WithAuthor("JRRT").WithTitle("The Lord Of The Rings"); 

等等。

如果你有很多属性,写这个很无聊:

 new Author(clone.ID, clone.firstName, clone.lastName...); 

查看你如何实现ICloneable接口的这个小例子:

这个例子来自: http : //csharp.2000things.com/2010/11/07/143-an-example-of-implementing-icloneable-for-deep-copies/

 public class Person : ICloneable { public string LastName { get; set; } public string FirstName { get; set; } public Address PersonAddress { get; set; } public object Clone() { Person newPerson = (Person)this.MemberwiseClone(); newPerson.PersonAddress = (Address)this.PersonAddress.Clone(); return newPerson; } } public class Address : ICloneable { public int HouseNumber { get; set; } public string StreetName { get; set; } public object Clone() { return this.MemberwiseClone(); } } Person herClone = (Person)emilyBronte.Clone(); 

问题1:是的,这会执行深层复制。 我建议使用ICloneable接口而不是静态function,因为它是标准的。

问题2:是的,原始集合不会随着您使用新集合对象而改变。

问题3:如果你在getter中返回集合,那么有人可以对其进行转换。 您可以在每次有人想要获取它时返回该集合的新副本,或者将其包装在一个容器中,该容器具有私有只读集合并且不公开该集合(但可以是可枚举的)。

在您的情况下,不确定您是否希望作者独立。 鉴于它们只能私下设置,分享它们的更新可能会很有用。 你唯一需要保持独立的是collections。 所以也许没有必要克隆作者。