In this thread:
How create a new deep copy (clone) of a List<T>?
the example of the person asking is how to create a deep copy of their List. I don't see anyone providing insight as to why it didn't work. Why did the new list behave as a reference to the old one? (Or at least it's elements).
I understand how references work in C++, and I thought the way they behave in C# is similar. But maybe I am missing something. I am still new to C#.
Here is a simplified version you can paste and run:
namespace OOPtest1
{
internal class Program
{
public class Book
{
public string title = "";
public Book(string title)
{
this.title = title;
}
}
static void Main(string[] args)
{
//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"; // this changes the books_1 element as well
books_2[1].title = "Six"; // this changes the books_1 element as well
}
}
}
I expected this to behave like a completely different collection.
Since it seems there is some familiarity here with C++, I want to open up talking about
structvsclasstypes. It's been a looong time, but IIRC in C++ the main difference between astructandclassis accessor defaults (publicvsprivate). In C#, the default for both isinternal, which is closer toprivate, but there is instead a semantic difference forstruct(value type) vsclass(reference type). It is strongly suggested to prefer using class as much as possible, and reservestructfor types that are both small and immutable.I start here, because it will be helpful for understanding some of what comes next, and because you could get closer to the behavior you expected by defining the
Booktype as astructinstead of aclass. But again: this is not recommended.Going further, and knowing that
Bookis a reference type, C# does not make the assumption that copying the memory data of a reference type object is enough to deep copy the object. Think open streams, file handles, com/serial ports, database connections, references to other memory/objects, items that expect unique guid members, etc. If you need to be able to take deep copies, you must provide the code to do that yourself, and there is no language support for the equivalent to a C++ copy constructor.Therefore, when creating the
books_2list based onbooks_1, while it does make a copy of the internal collection inbooks_1, only the references are copied. Each item inbooks_2initially refers to the same object as the equivalent item inbooks_1.I say "initially", because you can of course add and remove entire items from the
books_2list without effectingbooks_1. In this way, you do have a copy of the initial list; thebooks_2list is its own object, and not just a reference to the same list asbooks_1. But since the list is for a reference type you only have copies of the references, and underlying objects are shared.Therefore, in the original code, if you want to change the items for
books_2[0]andbooks_2[1], you could do it like this:This would leave
books_1completely unchanged. However, the[2]and[3]indexes would still share the same objects between the two lists.If you really wanted to fully deep copy the entire list, you might do this: