假设我有以下课程:
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<Author> authors;
private List<Author> copyofAuthors;
public string ISBN {get; private set; }
public Book(string bookTitle, List<Author> authors, string ISBN)
{
copyofAuthors = new List<Author>();
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<Authors>
集合的深层副本?我已经阅读了StackOverFlow上有关建议序列化的其他页面,以及我不熟悉并且看起来令人困惑的建议。
我跟着this link创建了我的克隆方法。
问题1:
以上实施是否被视为深层复制?如果是这样,可以这样做吗?我的意思是,构造函数中的foreach循环将作者复制到新的列表集合。
问题2:
如果我修改了copyofAuthor集合中的任何内容,不再引用原始集合是否正确?那么原始集合应保持不变?
更新#1:
public List<Author> Authors
{
get
{
return returnAuthors(authors);
}
}
private List<Author> returnAuthors(List<Author> copyList)
{
List<Author> getAuthors = new List<Author>();
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<Author> authors;
public string ISBN {get; private set; }
public Book(string bookTitle, ReadOnlyCollection<Author> authors, string ISBN)
{
this.bookTitle = bookTitle;
this.ISBN = ISBN;
//Is it okay to do this?
this.authors = authors;
}
public List<Author> Authors
{
get
{ //Create a shallow copy
return new ReadOnlyCollection<Author>(authors);
}
}
}
答案 0 :(得分:5)
在评论中对此进行排序太难了。
在你的书课上,你有两个问题要解决。
构造函数采用作者列表,但传递它的调用者可能会更改它。如果我们只是复制引用,那么调用者可能会意外地改变本书所持的列表。
这本书回复了作者名单。如果呼叫者再次向该列表添加内容,他们就会改变该书。
您可以使用不可变集合解决这两个问题。如果您还没有使用NuGet,请下载不可变集合库。
using System.Collections.Immutable;
...
public class Book
{
public string bookTitle {get; private set;}
private ImmutableList<Author> authors;
public IReadOnlyList<Author> Authors { get { return authors; } }
public string ISBN {get; private set; }
public Book(string bookTitle, IEnumerable<Author> authors, string ISBN)
{
this.authors = ImmutableList<Author>.Empty.AddRange(authors);
this.bookTitle = bookTitle;
this.ISBN = ISBN;
}
}
有。现在你创建了作者的序列的副本,所以如果调用者改变了那个序列,不用担心,你有副本。然后你分发一个由不可变集合实现的IReadOnlyList,所以没有人可以改变它。
结合更多事情。你问“这是对的吗?”
public class Book
{
private ReadOnlyCollection<Author> authors;
public Book(ReadOnlyCollection<Author> authors)
{
//Is it okay to do this?
this.authors = authors;
}
public List<Author> Authors
{
get
{ //Create a shallow copy
return new ReadOnlyCollection<Author>(authors);
}
}
(删除了多余的东西)。
不,由于一些原因,这不太对。首先,只读集合只是一个可变集合的包装器。您仍然处于调用者控制底层集合的情况,因此可以更改它。
其次,打字不太合适;你不能将ReadOnlyCollection转换为List。
我知道这令人困惑。这里有一个微妙的区别。只读集合就是:你只能读取它。这并不意味着别人无法写出来!这样的集合仍然是可变的,它只是不可变由你。不可变的集合是真正不可改变的;没人能改变它。
接下来:通过使作者和书籍都不可变,你做得很好。但是,如果你想改变呢?如您所知,更改不可变书籍意味着制作新书。但是你已经有了一本旧书;你怎么能有效地做到这一点?常见的模式是:
public class Book
{
public string Title {get; private set;}
private ImmutableList<Author> authors;
public IReadOnlyList<Author> Authors { get { return authors; } }
public string ISBN {get; private set; }
public Book(string title, IEnumerable<Author> authors, string ISBN) : this(
title,
ImmutableList<Author>.Empty.AddRange(authors),
ISBN) {}
public Book(string title, ImmutableList<Authors> 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<Author>.Empty, "");
}
现在你可以这样做:
Book tlotr = Book.Empty.WithAuthor("JRRT").WithTitle("The Lord Of The Rings");
等等。
答案 1 :(得分:0)
如果你有很多属性,写这个很无聊:
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();
答案 2 :(得分:0)
问题1: 是的,这会执行深层复制。我建议使用ICloneable接口而不是静态功能,因为它是标准的。
问题2: 是的,当您使用新的集合对象时,原始集合不会更改。
问题3: 如果你在getter中返回该集合,那么有人可以对其进行转换。您可以在每次有人想要获取它时返回该集合的新副本,或者将其包装在具有私有只读集合且不公开该集合的容器中(但可以枚举)。
在您的情况下,不确定您是否希望作者独立。鉴于它们只能私下设置,分享它们的更新可能会很有用。你唯一需要保持独立的是收藏。所以也许不需要克隆作者。