Question
I have a generic List<T> of objects in C# and want to clone it. The objects in the list can be cloned, but List<T> does not provide a Clone() method.
What is the simplest way to create a copy of the list, including cloned copies of its items when needed?
Short Answer
You will learn why List<T> has no Clone() method, how to create a new list with the same items, and how to create a true deep copy by cloning each object. You will also learn when each approach is appropriate.
Concept
A List<T> is a collection object that stores a sequence of values. To "clone a list," you must first decide what should be copied:
- The list container only: create a new
List<T>containing the same values. - The list and every referenced object inside it: create a new list and clone each item.
For value types such as int, decimal, and DateTime, copying the values is usually enough because the values themselves are stored directly.
For reference types such as class instances, copying a list normally copies references. The new list is independent, but both lists point to the same objects. This is called a shallow copy.
A deep copy creates separate item objects as well. Changes to an item in one list then do not affect the corresponding item in the other list.
List<T> does not implement a general-purpose Clone() method because it cannot know the correct cloning behavior for every possible T. Some objects should share references, some need custom copying rules, and some cannot be safely cloned at all.
Mental Model
Think of a list as a folder containing address cards.
- A shallow copy makes a new folder and puts photocopies of the card locations into it. Both folders lead to the same physical cards. Editing a card affects what both folders show.
- A deep copy makes a new folder and creates a separate photocopy of every card. Editing a card in one folder does not change the matching card in the other.
The folders are always separate after copying. The important question is whether the cards inside are shared or independently copied.
Syntax and Examples
Create a new list with the List<T> constructor:
List<string> names = new List<string> { "Ava", "Noah" };
List<string> copiedNames = new List<string>(names);
For strings, this is usually sufficient. Although string is a reference type, strings are immutable: their contents cannot be changed after creation.
For a list of mutable class objects, this is a shallow copy:
public class Product
{
public string Name { get; set; } = "";
}
List<Product> products = new List<Product>
{
new Product { Name = "Keyboard" }
};
List<Product> copiedProducts = new List<Product>(products);
copiedProducts[0].Name = "Mechanical Keyboard";
Console.WriteLine(products[0].Name); // Mechanical Keyboard
The two lists are different list objects, but index 0 in both lists refers to the same Product.
To make a deep copy, create a new item for every source item. For example, if the type exposes a Clone method:
List<Product> clonedProducts = products
.Select(product => product.Clone())
.ToList();
This requires Product.Clone() to return a properly independent Product instance.
Step by Step Execution
Consider this shallow-copy example:
var original = new List<Customer>
{
new Customer { Name = "Mina" }
};
var copy = new List<Customer>(original);
copy[0].Name = "Mina Patel";
Console.WriteLine(original[0].Name);
public class Customer
{
public string Name { get; set; } = "";
}
Execution steps:
originalis created with oneCustomerobject.new List<Customer>(original)creates a new list container.- The reference to the
Customeris copied intocopy. copy[0]andoriginal[0]refer to the sameCustomerinstance.- Changing
copy[0].Namechanges that shared object. - The output is
Mina Patel.
Now compare it with a deep copy:
var deepCopy = original
.Select(customer => new Customer { Name = customer.Name })
.ToList();
deepCopy[0].Name = "Different Customer";
Console.WriteLine(original[0].Name); // Mina Patel
Here, the Select expression creates a new Customer for every source customer, so the two lists no longer share item instances.
Real World Use Cases
- Editing screens and forms: Copy an object or list before the user edits it, so Cancel can discard changes.
- API request processing: Transform incoming data into separate internal models without mutating the original request objects.
- Undo/redo history: Store snapshots of mutable application state. A deep copy may be necessary if nested objects can change.
- Data transformation: Create a new list of DTOs, view models, or report rows from database entities.
- Concurrent or background work: Use an independent snapshot of data when one task must not observe later changes made by another part of the application.
- Configuration templates: Start from default settings, then make a separate copy for each user, tenant, or environment.
Real Codebase Usage
In production code, developers usually choose copying behavior explicitly instead of relying on a vague Clone() operation.
Shallow-copy a collection before changing its membership
Use this when objects can be shared but you need to add or remove entries independently:
var updatedUsers = new List<User>(existingUsers);
updatedUsers.Add(newUser);
Removing from updatedUsers does not remove from existingUsers.
Project source objects into new objects
This is a clear and common deep-copy-like pattern:
var orderDtos = orders
.Select(order => new OrderDto
{
Id = order.Id,
Total = order.Total,
Status = order.Status
})
.ToList();
Put copy logic on the type when it owns the rules
public class Address
{
public string Street { get; init; } = "";
public string City { get; init; } = "";
public Address Copy() => new Address
{
Street = Street,
City = City
};
}
A clearly named Copy method can be easier to understand than ICloneable, because callers know its return type and can document whether nested objects are also copied.
Validate while copying
When source data may be missing or invalid, handle it deliberately:
var copies = source
.Where(item => item is not null)
.Select(item => item.Copy())
.ToList();
Whether null items should be skipped, rejected, or preserved depends on the application's rules.
Common Mistakes
Assuming new List<T>(source) deep-copies objects
This creates a new list, not new instances of mutable class items:
var copy = new List<Product>(products); // Shallow copy
Avoid the mistake by constructing a new item for each source item when item independence is required.
Using MemberwiseClone() as a full deep copy
MemberwiseClone() creates a shallow copy. Nested references remain shared.
public Order ShallowCopy()
{
return (Order)MemberwiseClone();
}
If Order contains a mutable Address, both orders will still reference the same address unless you copy it too.
Forgetting nested mutable objects
This is not fully deep if Address is mutable:
var copiedCustomer = new Customer
{
Name = source.Name,
Address = source.Address // Shared reference
};
Copy nested mutable data as well:
Address = source.Address.Copy()
Calling Clone() without a type-safe contract
ICloneable.Clone() returns object, so it requires a cast and does not promise whether the result is shallow or deep:
var productCopy = (Product)product.Clone();
Prefer a type-specific method such as Product Copy() or a copy constructor when you control the class design.
Using serialization only to copy objects
Serialization-based copying can be slow, fragile, and may fail for types that are not serializable. Use explicit copy logic for normal application models.
Comparisons
| Approach | New list container? | New item objects? | Best use |
|---|---|---|---|
new List<T>(source) | Yes | No for reference types | Change list membership independently |
source.ToList() | Yes | No for reference types | LINQ pipelines and concise shallow copies |
source.Select(x => x.Copy()).ToList() | Yes | Yes, if Copy() is correct | Independent mutable objects |
Copy constructor, such as new Product(source) | Depends on use | Yes | Explicit, type-safe copying |
MemberwiseClone() | Not applicable | No, it is shallow | Limited internal cloning scenarios |
new List<T>(source) and source.ToList() have equivalent copying behavior for a normal enumerable source: both create a new list and preserve the same item references.
For value types, shallow versus deep copying is generally not a concern because each list receives its own copied values. For mutable reference types, it is crucial.
Cheat Sheet
// Shallow copy: new list, same item references
var copy = new List<T>(source);
// Also a shallow copy, using LINQ
var copy = source.ToList();
// Deep copy: each item provides a type-safe copy method
var deepCopy = source.Select(item => item.Copy()).ToList();
// Deep copy with a copy constructor
var deepCopy = source.Select(item => new Item(item)).ToList();
List<T>does not have a built-inClone()method.- A shallow copy separates the list structure, not mutable objects within it.
- Use a deep copy only when item changes must be isolated.
- Copy nested mutable objects too when implementing a deep copy.
- Immutable items, such as
string, can safely be shared. - Prefer a type-safe
Copy()method or copy constructor over ambiguous cloning contracts when possible.
FAQ
Does new List<T>(oldList) clone the objects in C#?
No. It creates a new list but copies each element value. For class instances, those values are references to the same objects.
Is ToList() a deep copy in C#?
No. ToList() creates a new list container, but it does not clone referenced objects.
Why does List<T> not implement ICloneable?
A list cannot decide whether each item should be copied shallowly, deeply, or not at all. That behavior depends on the item type and application rules.
How do I deep-copy a List<T>?
Create a new list by transforming each source item into a new independent item, commonly with Select(item => item.Copy()).ToList() or a copy constructor.
Do I need a deep copy for List<string>?
Usually no. Strings are immutable, so sharing string instances is safe.
Does copying a list copy its capacity too?
No. A new list is created with enough capacity for its copied elements; its internal storage is not the same as the original list's storage.
Should I use ICloneable for my own classes?
It can work, but ICloneable.Clone() returns object and does not specify shallow or deep behavior. A type-specific Copy() method or copy constructor is often clearer.
Mini Project
Description
Build a small product-catalog snapshot utility. It demonstrates the difference between copying a list structure and creating independently editable product objects, including a nested mutable Category object.
Goal
Create a deep copy of a product list, modify the copied data, and verify that the original catalog remains unchanged.
Requirements
Create Category and Product classes with copy methods.
Create an original catalog containing at least two products.
Create a deep-copied catalog by copying every product.
Modify a product name and a nested category name in the copied catalog.
Print values from both catalogs to prove that the original was not changed.
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
Best Way to Repeat a Character in C#: Building Repeated Strings Efficiently
Learn the best way to repeat a character in C#, compare StringBuilder, string concatenation, and simpler built-in options.
C# Access Modifiers and static: public, private, protected, and Defaults
Learn how C# public, private, protected, and default access control visibility, and how static differs from instance members.