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

168,549

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

Solution 5

You can use this:

var newList= JsonConvert.DeserializeObject<List<Book>>(list.toJson());
Share:
168,549

Related videos on Youtube

TheScholar
Author by

TheScholar

Updated on July 08, 2022

Comments

  • TheScholar
    TheScholar almost 2 years

    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.

    • K-ballo
      K-ballo over 11 years
      Something like CopyTo?
    • Alexei Levenkov
      Alexei Levenkov over 11 years
      This type of copying is normally called deep copy. If you feel "hard copy" is something different that you actually need in your case - please undo my edit and add your definition of the term.
    • Mark Byers
      Mark Byers over 11 years
      A hard copy means that you print it out onto a physical sheet of paper.
    • Alexei Levenkov
      Alexei Levenkov over 11 years
      Please check out other related questions about deep-copy asked before - stackoverflow.com/questions/tagged/…
    • TheScholar
      TheScholar over 11 years
      I meant deep-copy indeed, sorry I'm still a newbies.
    • slugster
      slugster over 11 years
      Personally I like to use a short serialisation & deserialisation routine that achieves the deep copy - de/serialisation is slow(ish), but this works no matter how many properties the object has. Unfortunately I'm on holiday so I can't just post the code.
    • Dave Hillier
      Dave Hillier over 11 years
      Deep-copy each element of the list. It depends on the contents of your list.
    • CodesInChaos
      CodesInChaos over 9 years
      IMO deep cloning is inherently not neat.
    • Vidyesh
      Vidyesh over 4 years
      just create a new list with linq
  • TheScholar
    TheScholar over 11 years
    What if the Book object was more complex and had thousands of other properties?
  • Alexei Levenkov
    Alexei Levenkov over 11 years
    +1, @TheScholar - than you either create copy contructor or implement some other way to create Deep-copy of an object.
  • Mark Byers
    Mark Byers over 11 years
    @TheScholar: Then it would be a badly designed class. Thousands of properties... are you serious?
  • TheScholar
    TheScholar over 11 years
    @MarkByers it was a figure of speeach of course. My example provide a single property for the sake of keeping the example simple and easy to read.
  • Mark Byers
    Mark Byers over 11 years
    The new question can be simplified to "how do I copy a single object in C#?"
  • TheScholar
    TheScholar over 11 years
    Thanks a lot guys for the efforts. I realize that copying data or attempting to swap them is a bit more complicated than I expected.
  • Ocean Airdrop
    Ocean Airdrop about 9 years
    in my instance this was the easiest approach. eg: var tagsToRead = new List<string>( tagsFailed.ToArray());
  • Tom Lint
    Tom Lint over 7 years
    Except the objects in the array are still references to the items in the original list.
  • Robert F.
    Robert F. over 7 years
    Why is the ToList() not enough? Why do you need the Select statement too?
  • AxelWass
    AxelWass over 7 years
    @RoberF. ToList() is not enough because the OP asked for a deep copy. The ToList() alone would be a shallow copy. The Select copy not only the list, but the objects inside it.
  • Anup Sharma
    Anup Sharma about 6 years
    When everything else failed, this one helped me. I had an object type property and there was no other way which could make a copy of that property. Thanks for the snippet.
  • user3496060
    user3496060 almost 6 years
    This works, but note that one can't just pass new List<Book>(books_2.ToArray()); to a function. It has to be done just like it's posted and then "books_2" could be passed.
  • jrandomuser
    jrandomuser almost 6 years
    This does not work for reference types. What you end up with is a pointer to the original. If you update a property on one you have now changed both!
  • Bence Végert
    Bence Végert almost 6 years
    This is just works if you have easy typed properties, if you have a subclass instance eg. you should implement the iserialiable.
  • Akash
    Akash over 5 years
    That's the cleanest solution to deep copy an array. Thanks man. You saved lot of my time.
  • Vidyesh
    Vidyesh over 4 years
    this is too expensive
  • Hossein Ebrahimi
    Hossein Ebrahimi almost 3 years
    As this does not work for reference types, why not just call ToList() on a list of value types, I don't see a difference.
  • Jan
    Jan almost 3 years
    Why should you not use the default ICloneable interface but instead use a custom generic one?
  • Bernoulli IT
    Bernoulli IT almost 3 years
    This is not a copy, rather a shadow list with references to the same items in the list. Modifying an item in List A will modify the same item in List B because they are the same.
  • ToolmakerSteve
    ToolmakerSteve almost 3 years
    @Vidyesh - Its too expensive compared to what? The spec is to create a list with new objects in it. The only way to do that, is to create new objects. If you don't need "new objects", then you don't need "deep copy" - you simply do .ToList(). But that isn't the subject of this Q&A.
  • d0rf47
    d0rf47 almost 2 years
    I would also like to know why I should not use ICloneable?
  • svick
    svick almost 2 years
    @d0rf47 The documentation explains it: "Because callers of Clone() cannot depend on the method performing a predictable cloning operation, we recommend that ICloneable not be implemented in public APIs."
  • d0rf47
    d0rf47 almost 2 years
    @svick I see, but lets just say for arguments sake, I could make mine perform the deep copy if i wanted to just for a small program that no one else is ever going to use right? It seems like its just not recommended since they implementation method isn't enforced and can therefor vary if returning deep vs shallow, is this a correct understanding?