In C#, collections can be mutable, read-only, or immutable. Understanding their differences is important when designing thread-safe applications, exposing data through APIs, and preventing accidental modifications.
1. What is the difference between mutable and immutable collections?
What is a Mutable Collection?
A mutable collection allows you to add, remove, or update elements after the collection is created.
Examples:
-
List<T> -
Dictionary<TKey, TValue> -
HashSet<T>
What is an Immutable Collection?
An immutable collection cannot be modified after creation. Operations such as Add(), Remove(), and SetItem() return a new collection value instead of changing the existing collection.
Examples:
-
ImmutableList<T> -
ImmutableDictionary<TKey, TValue> -
ImmutableHashSet<T>
Diagram
Mutable Collection
------------------
List<int> numbers = [10, 20, 30]
Add(40)
|
v
Original List = [10, 20, 30, 40]
Same collection modified.
Immutable Collection
--------------------
ImmutableList<int> numbers = [10, 20, 30]
Add(40)
|
v
Original = [10, 20, 30]
New = [10, 20, 30, 40]
Original collection remains unchanged.
Example Program
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
class Program
{
static void Main()
{
// Mutable collection
List<int> numbers = new() { 10, 20, 30 };
numbers.Add(40);
Console.WriteLine(string.Join(", ", numbers));
// Immutable collection
ImmutableList<int> immutable =
ImmutableList.Create(10, 20, 30);
ImmutableList<int> updated = immutable.Add(40);
Console.WriteLine(string.Join(", ", immutable));
Console.WriteLine(string.Join(", ", updated));
}
}
Output:
10, 20, 30, 40
10, 20, 30
10, 20, 30, 40
Explanation
-
List<T>.Add()modifies the existing collection. -
ImmutableList<T>.Add()returns a new immutable list. -
The original immutable collection remains unchanged.
-
Immutable collections can reuse internal data structures, so they do not necessarily copy every element.
Comparison
|
Feature |
Mutable |
Immutable |
|---|---|---|
|
Modification |
Allowed |
Not allowed in place |
|
Add/Remove |
Modifies existing collection |
Returns new collection |
|
Thread safety |
Requires synchronization for concurrent modifications |
Safe for concurrent reads |
|
Performance |
Generally faster for frequent updates |
Additional cost for updates |
|
Examples |
|
|
Key Points
-
Mutable: Existing collection can change.
-
Immutable: Existing collection cannot change.
-
Immutable collections are useful when multiple threads share data.
-
Immutability applies to the collection structure, not automatically to mutable objects stored inside it.
-
Prefer mutable collections for frequent modifications and immutable collections for stable shared data.
2. What is the difference between ReadOnlyCollection<T> and ImmutableList<T>?
What is ReadOnlyCollection<T>?
ReadOnlyCollection<T> provides a read-only wrapper around an existing list.
Consumers cannot directly add, remove, or replace elements through the wrapper.
However, if the original list changes, those changes are reflected in the read-only collection.
Namespace:
using System.Collections.ObjectModel;
What is ImmutableList<T>?
ImmutableList<T> is an immutable collection whose contents cannot be changed after creation.
Methods such as Add() and Remove() return new immutable lists.
Namespace:
using System.Collections.Immutable;
Diagram
ReadOnlyCollection<T>
---------------------
Original List
[10, 20, 30]
|
| ReadOnlyCollection wrapper
v
ReadOnly View
[10, 20, 30]
Original List.Add(40)
|
v
Original List = [10, 20, 30, 40]
ReadOnly View = [10, 20, 30, 40]
Changes are visible.
ImmutableList<T>
----------------
Original ImmutableList
[10, 20, 30]
|
| Add(40)
v
New ImmutableList
[10, 20, 30, 40]
Original remains unchanged.
Example Program
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Immutable;
class Program
{
static void Main()
{
// ReadOnlyCollection
List<int> numbers = new() { 10, 20, 30 };
ReadOnlyCollection<int> readOnly =
numbers.AsReadOnly();
numbers.Add(40);
Console.WriteLine("ReadOnly:");
Console.WriteLine(string.Join(", ", readOnly));
// ImmutableList
ImmutableList<int> immutable =
ImmutableList.Create(10, 20, 30);
ImmutableList<int> updated = immutable.Add(40);
Console.WriteLine("Original Immutable:");
Console.WriteLine(string.Join(", ", immutable));
Console.WriteLine("Updated Immutable:");
Console.WriteLine(string.Join(", ", updated));
}
}
Output:
ReadOnly:
10, 20, 30, 40
Original Immutable:
10, 20, 30
Updated Immutable:
10, 20, 30, 40
Explanation
ReadOnlyCollection:
ReadOnlyCollection<int> readOnly = numbers.AsReadOnly();
-
Creates a read-only view of
numbers. -
Does not create an independent copy.
-
Changes to
numbersare reflected inreadOnly.
ImmutableList:
ImmutableList<int> updated = immutable.Add(40);
-
Creates a new immutable list value.
-
Does not change
immutable. -
The original list remains unchanged.
Comparison
|
Feature |
|
|
|---|---|---|
|
Type |
Read-only wrapper |
Immutable collection |
|
Direct modification |
Not allowed |
Not allowed |
|
Underlying data can change |
Yes |
No |
|
Reflects changes to original list |
Yes |
No |
|
|
Not supported publicly |
Returns new list |
|
Thread safety |
Depends on underlying list |
Safe for concurrent reads |
|
Best use |
Exposing a list without write access |
Sharing stable collection data |
Real-Time Scenario
Suppose an ASP.NET Core application maintains a list of supported countries.
ReadOnlyCollection:
private readonly List<string> _countries =
new() { "India", "USA" };
public IReadOnlyList<string> Countries =>
_countries.AsReadOnly();
Other components cannot modify the list through the returned interface, but the owning class can still change _countries.
ImmutableList:
private ImmutableList<string> _countries =
ImmutableList.Create("India", "USA");
public ImmutableList<string> Countries => _countries;
Callers cannot change the published list. The owning class can publish a new list value when necessary.
Key Points
-
ReadOnlyCollection<T>is a wrapper, not an immutable snapshot. -
ImmutableList<T>guarantees that its collection contents remain unchanged. -
ReadOnlyCollection<T>reflects modifications to its underlying list. -
ImmutableList<T>returns a new collection when updated. -
Use
ImmutableList<T>when collection immutability is required.
3. What is the difference between IReadOnlyList<T> and ImmutableList<T>?
What is IReadOnlyList<T>?
IReadOnlyList<T> is an interface that provides read-only access to an ordered collection.
It supports:
-
Accessing elements using an index.
-
Reading the number of elements using
Count. -
Iterating using
foreach.
It does not expose methods to add, remove, or replace elements.
However, it does not guarantee that the underlying collection is immutable.
What is ImmutableList<T>?
ImmutableList<T> is a concrete immutable collection implementation.
It implements IReadOnlyList<T> and provides methods that produce new immutable collection values.
Diagram
IReadOnlyList<T>
|
Read-only interface
|
-------------------
| |
v v
List<T> ImmutableList<T>
| |
Mutable Immutable
| |
Can change through Cannot change
another reference existing contents
Example Program
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
class Program
{
static void Main()
{
// IReadOnlyList backed by mutable List
List<int> numbers = new() { 10, 20, 30 };
IReadOnlyList<int> readOnly = numbers;
numbers.Add(40);
Console.WriteLine("IReadOnlyList:");
Console.WriteLine(string.Join(", ", readOnly));
// ImmutableList
ImmutableList<int> immutable =
ImmutableList.Create(10, 20, 30);
ImmutableList<int> updated = immutable.Add(40);
Console.WriteLine("ImmutableList:");
Console.WriteLine(string.Join(", ", immutable));
}
}
Output:
IReadOnlyList:
10, 20, 30, 40
ImmutableList:
10, 20, 30
Explanation
IReadOnlyList:
IReadOnlyList<int> readOnly = numbers;
-
readOnlyreferences the existing mutable list. -
You cannot call
readOnly.Add(40). -
But
numbers.Add(40)is allowed. -
Therefore, the changes are visible through
readOnly.
ImmutableList:
ImmutableList<int> updated = immutable.Add(40);
-
immutableremains unchanged. -
updatedcontains the additional element. -
The collection guarantees immutability of its structure.
Comparison
|
Feature |
|
|
|---|---|---|
|
Type |
Interface |
Concrete class |
|
Read-only access |
Yes |
Yes |
|
Guarantees immutability |
No |
Yes |
|
Underlying collection |
May be mutable |
Immutable |
|
Index access |
Yes |
Yes |
|
|
Not available through interface |
Returns new list |
|
Can reference |
Yes |
No |
|
Primary purpose |
Read-only API contract |
Immutable data storage |
Real-Time Scenario
In Clean Architecture, suppose an application service returns a collection of employees.
Using IReadOnlyList:
public IReadOnlyList<Employee> GetEmployees()
{
return _employees.AsReadOnly();
}
This exposes read-only access, but the service can still modify _employees.
Using ImmutableList:
public ImmutableList<Employee> GetEmployees()
{
return _employees.ToImmutableList();
}
This returns an immutable collection snapshot. Later changes to the original list will not affect the returned collection.
Note that if Employee is a mutable class, its properties may still be modified. Collection immutability does not make the employee objects immutable.
Key Points
-
IReadOnlyList<T>is an interface, not a collection implementation. -
It restricts modifications through that interface.
-
ImmutableList<T>is an actual immutable collection. -
ImmutableList<T>implementsIReadOnlyList<T>. -
Use
IReadOnlyList<T>for flexible read-only method contracts. -
Use
ImmutableList<T>when you need an immutable collection guarantee.
Final Comparison: All Three Collections
|
Feature |
|
|
|
|---|---|---|---|
|
Mutable |
Yes |
No, through wrapper |
No |
|
Underlying data can change |
Yes |
Yes |
No |
|
Add/Remove directly |
Yes |
No |
Returns new list |
|
Reflects external changes |
Yes |
Yes |
No |
|
Safe for concurrent reads without writes |
Yes |
Yes |
Yes |
|
Best use |
Frequent updates |
Read-only view |
Immutable shared data |
Important Interview Questions and Answers
Q1. Is IReadOnlyList<T> immutable?
No. It provides read-only access but does not guarantee immutability.
Q2. Can ReadOnlyCollection<T> change?
Yes. If the underlying list changes, the read-only wrapper reflects those changes.
Q3. Does ImmutableList<T>.Add() modify the original list?
No. It returns a new immutable list.
Q4. Are immutable collections thread-safe?
Immutable collection instances are safe for concurrent reads because their structure cannot change. However, mutable elements and concurrent replacement of shared references may still require synchronization.
Q5. Which collection should be used in enterprise applications?
-
List<T>— Frequent additions, removals, and updates. -
ReadOnlyCollection<T>— Exposing an internally managed list without direct write access. -
IReadOnlyList<T>— Defining read-only service or API contracts. -
ImmutableList<T>— Sharing collection data that must not change.
Interview takeaway: Read-only means you cannot modify the collection through the given interface or wrapper. Immutable means the existing collection value itself cannot be changed.