Keywords: c# | list | clone | deep-copy
Abstract: This article explains how to perform a deep copy of a List<T> in C#, covering methods like LINQ Select and ConvertAll, and introducing the ICloneable interface for object cloning. Aimed at developers seeking to avoid reference sharing issues in collections, with detailed analysis based on sample code and best practice recommendations.
Understanding Shallow vs Deep Copy in C# Lists
In C#, the List<T> class provides a constructor that accepts an IEnumerable<T> to create a new list, but this results in a shallow copy. A shallow copy means the list itself is a new object, but the elements within it are references to the original objects. Modifying the copied list can inadvertently change the original elements, potentially causing bugs.
Deep Copy Solution Using LINQ
To achieve a deep copy, where both the list and its elements are independent, you can use LINQ's Select method combined with ToList. This approach creates new instances of the objects to avoid reference sharing.
List<Book> books_2 = books_1.Select(book => new Book(book.title)).ToList();
This code iterates over each Book in books_1, creates a new Book with the same title, and collects them into a new list. As a result, books_2 and books_1 will not affect each other.
Alternative Method: Using ConvertAll
Another efficient way is to use the ConvertAll method of List<T>, which returns a new list after applying a converter function.
List<Book> books_2 = books_1.ConvertAll(book => new Book(book.title));
ConvertAll is similar to Select but is specific to lists and may offer better performance in some cases.
Extending with ICloneable Interface
For more complex classes, implementing a cloning interface can enhance flexibility. Inspired by other answers, you can define a generic ICloneable<T> interface.
public interface ICloneable<T>
{
T Clone();
}
public class Book : ICloneable<Book>
{
public string Title { get; set; }
public Book Clone()
{
return new Book { Title = this.Title };
}
}
Then, you can clone the list as follows:
List<Book> books_2 = books_1.Select(book => book.Clone()).ToList();
Practical Example Based on Provided Code
Using the original code snippet, here's how to modify it for deep copy:
using System;
using System.Collections.Generic;
using System.Linq;
namespace clone_test_01
{
public class Book
{
public string Title { get; set; }
public Book(string title)
{
this.Title = title;
}
}
public partial class MainForm : Form
{
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"));
// Deep copy using LINQ
List<Book> books_2 = books_1.Select(book => new Book(book.Title)).ToList();
// Modifications to books_2 will not affect books_1
books_2[0].Title = "Five";
books_2[1].Title = "Six";
textBox1.Text = books_1[0].Title; // Still "One"
textBox2.Text = books_1[1].Title; // Still "Two"
}
}
}
Conclusion
Creating a deep copy of a List<T> in C# requires manual instantiation of new objects. The Select with ToList and ConvertAll methods are straightforward solutions. For scalable applications, implementing a cloning interface like ICloneable<T> can improve maintainability. Always consider the complexity of the objects when choosing a cloning strategy.