How create a new deep copy (clone) of a List<T>?

In the following piece of code,

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;
        }
    }

}

I use a Book object type to create a List<T> and I populate it with a few items giving them a unique title (from 'one' to 'five').

Then I create List<Book> books_2 = new List<Book>(books_1).

From this point, I know it's a clone of the list object, BUT the book objects from book_2 are still a reference from the book objects in books_1. It's proven by making changes on the two first elements of books_2, and then checking those same elements of book_1 in a TextBox.

books_1[0].title and books_2[1].title have indeed been changed to the new values of books_2[0].title and books_2[1].title.

NOW THE QUESTION

How do we create a new hard copy of a List<T>? The idea is that books_1 and books_2 become completely independent of each other.

I'm disappointed Microsoft didn't offer a neat, fast and easy solution like Ruby are doing with the clone() method.

What would be really awesome from helpers is to use my code and alter it with a workable solution so it can be compiled and work. I think it will truly help newbies trying to understand offered solutions for this issue.

EDIT: Note that the Book class could be more complex and have more properties. I tried to keep things simple.


Solution 1:

You need to create new Book objects then put those in a new List:

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

Update: Slightly simpler... List<T> has a method called ConvertAll that returns a new list:

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

Solution 2:

Create a generic ICloneable<T> interface which you implement in your Book class so that the class knows how to create a copy of itself.

public interface ICloneable<T>
{
    T Clone();
}

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

You can then use either the linq or ConvertAll methods that Mark mentioned.

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

or

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

Solution 3:

I'm disappointed Microsoft didn't offer a neat, fast and easy solution like Ruby are doing with the clone() method.

Except that does not create a deep copy, it creates a shallow copy.

With deep copying, you have to be always careful, what exactly do you want to copy. Some examples of possible issues are:

  1. Cycle in the object graph. For example, Book has an Author and Author has a list of his Books.
  2. Reference to some external object. For example, an object could contain open Stream that writes to a file.
  3. Events. If an object contains an event, pretty much anyone could be subscribed to it. This can get especially problematic if the subscriber is something like a GUI Window.

Now, there are basically two ways how to clone something:

  1. Implement a Clone() method in each class that you need cloned. (There is also ICloneable interface, but you should not use that; using a custom ICloneable<T> interface as Trevor suggested is okay.) If you know that all you need is to create a shallow copy of each field of this class, you could use MemberwiseClone() to implement it. As an alternative, you could create a “copy constructor”: public Book(Book original).
  2. Use serialization to serialize your objects into a MemoryStream and then deserialize them back. This requires you to mark each class as [Serializable] and it can also be configured what exactly (and how) should be serialized. But this is more of a “quick and dirty” solution, and will most likely also be less performant.

Solution 4:

Well,

If you mark all involved classes as serializable you can :

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);      
} 

Source:

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