How create a new deep copy (clone) of a List<T>?
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:
- Cycle in the object graph. For example,
Book
has anAuthor
andAuthor
has a list of hisBook
s. - Reference to some external object. For example, an object could contain open
Stream
that writes to a file. - 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:
- Implement a
Clone()
method in each class that you need cloned. (There is alsoICloneable
interface, but you should not use that; using a customICloneable<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 useMemberwiseClone()
to implement it. As an alternative, you could create a “copy constructor”:public Book(Book original)
. - 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:
Solution 5
You can use this:
var newList= JsonConvert.DeserializeObject<List<Book>>(list.toJson());
Related videos on Youtube
TheScholar
Updated on July 08, 2022Comments
-
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 aList<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 inbooks_1
. It's proven by making changes on the two first elements ofbooks_2
, and then checking those same elements ofbook_1
in aTextBox
.books_1[0].title and books_2[1].title
have indeed been changed to the new values ofbooks_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 thatbooks_1
andbooks_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 over 11 yearsSomething like
CopyTo
? -
Alexei Levenkov over 11 yearsThis 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 over 11 yearsA hard copy means that you print it out onto a physical sheet of paper.
-
Alexei Levenkov over 11 yearsPlease check out other related questions about deep-copy asked before - stackoverflow.com/questions/tagged/…
-
TheScholar over 11 yearsI meant deep-copy indeed, sorry I'm still a newbies.
-
slugster over 11 yearsPersonally 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 over 11 yearsDeep-copy each element of the list. It depends on the contents of your list.
-
CodesInChaos over 9 yearsIMO deep cloning is inherently not neat.
-
Vidyesh over 4 yearsjust create a new list with linq
-
-
TheScholar over 11 yearsWhat if the Book object was more complex and had thousands of other properties?
-
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 over 11 years@TheScholar: Then it would be a badly designed class. Thousands of properties... are you serious?
-
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 over 11 yearsThe new question can be simplified to "how do I copy a single object in C#?"
-
TheScholar over 11 yearsThanks 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 about 9 yearsin my instance this was the easiest approach. eg: var tagsToRead = new List<string>( tagsFailed.ToArray());
-
Tom Lint over 7 yearsExcept the objects in the array are still references to the items in the original list.
-
Robert F. over 7 yearsWhy is the
ToList()
not enough? Why do you need theSelect
statement too? -
AxelWass over 7 years@RoberF.
ToList()
is not enough because the OP asked for a deep copy. TheToList()
alone would be a shallow copy. TheSelect
copy not only the list, but the objects inside it. -
Anup Sharma about 6 yearsWhen 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 almost 6 yearsThis 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 almost 6 yearsThis 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 almost 6 yearsThis is just works if you have easy typed properties, if you have a subclass instance eg. you should implement the iserialiable.
-
Akash over 5 yearsThat's the cleanest solution to deep copy an array. Thanks man. You saved lot of my time.
-
Vidyesh over 4 yearsthis is too expensive
-
Hossein Ebrahimi almost 3 yearsAs 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 almost 3 yearsWhy should you not use the default ICloneable interface but instead use a custom generic one?
-
Bernoulli IT almost 3 yearsThis 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 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 almost 2 yearsI would also like to know why I should not use ICloneable?
-
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 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?