Sunday, June 21, 2026

C# When to use IReadOnlyCollection generic collection

You should use IReadOnlyCollection<T> when you want to expose a collection that can be counted, but you want to explicitly signal to the consumer that they are not allowed to modify the contents (i.e., no adding, removing, or clearing elements).

It acts as a lightweight, read-only wrapper around a data structure, providing only two guarantees:
  1. You can iterate over it (IEnumerable).
  2. You can ask for its size instantly without iterating (Count).
Key Scenarios for IReadOnlyCollection<T>
1. Public Properties in Classes (Encapsulation)
If your class maintains an internal list, you should never expose it directly as a List<T>. If you do, external code can modify your class's internal state without its knowledge.
public class Order
{
    // Internal state can be modified freely inside the class
    private readonly List<OrderItem> _items = new();

    // External code can see the items and count them, but cannot add/remove them
    public IReadOnlyCollection<OrderItem> Items => _items; 
}
2. When IEnumerable<T> is Too Limiting
IEnumerable<T> is great for lazy evaluation (like LINQ queries), but it doesn't know its own size. If a developer wants to know how many items are in an IEnumerable<T>, they have to call the .Count() extension method, which forces the CPU to loop through the entire collection to count them O(1) time complexity).

By using IReadOnlyCollection<T>, you provide a Count property that is a direct lookup O(1) time complexity), while still keeping the collection read-only.

The Collection Interface Hierarchy
To understand exactly where IReadOnlyCollection<T> fits, it helps to see the progression of collection power in .NET:
  1. IEnumerable<T> can be looped through using foreach.
  2. IReadOnlyCollection<T> can be looped through, and you know how many items you have instantly.
  3. IReadOnlyList<T> can be looped through, you know the size, and you can access the elements by index (e.g., list[3]).
  4. ICollection<T> / IList<T> provides Full access. You can loop, read, index, add, delete, and clear.
Summary Checklist: When to choose it?

Use this Interface... If you need the consumer to...
IEnumerable<T> Stream data lazily (like from a database) or just loop through elements sequentially.
IReadOnlyCollection<T> Safely view a fixed snapshot of a collection and check its size, without indexing or modifying it.
IReadOnlyList<T> View a collection safely, check its size, and look up items via random access index (e.g., items[i]).
List<T> / ICollection<T> Have full permission to add, remove, and mutate the underlying data structure.

Important Caveat: IReadOnlyCollection<T> means the collection structure is read-only (you can't add or remove items). However, if T is a mutable class object, a developer can still change the properties of an item inside the collection (e.g., collection.First().Status = "Changed"). It does not make the underlying objects immutable!

No comments:

Post a Comment

Hot Topics