如何创建列表集的深层副本
假设我有以下课程:
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。 所以也许没有必要克隆作者。