ReadOnlyCollection
IEnumerable
Member Collections
C#
.NET

ReadOnlyCollection or IEnumerable for exposing member collections?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

When a class exposes an internal collection, the return type is not just syntax. It defines what callers are allowed to assume about ordering, indexing, mutability, and evaluation cost. Choosing between IEnumerable and ReadOnlyCollection is really a contract design decision, and the right contract depends on how consumers use the data.

Start with the API Contract, Not the Backing Type

A frequent mistake is exposing whatever data structure exists internally. If the class happens to use List, developers return List directly and lock the API to a mutable representation forever. A better approach is to expose the smallest interface that still supports real consumer needs.

If consumers only iterate, IEnumerable may be enough. If they need stable count and index access, IReadOnlyList or ReadOnlyCollection is a clearer contract.

csharp
1using System;
2using System.Collections.Generic;
3using System.Collections.ObjectModel;
4
5public sealed class Cart
6{
7    private readonly List<string> _items = new();
8
9    // Minimal contract: callers can enumerate.
10    public IEnumerable<string> Items => _items;
11
12    // Stronger contract: callers can index and count.
13    public ReadOnlyCollection<string> ItemsView => _items.AsReadOnly();
14
15    public void Add(string item)
16    {
17        if (string.IsNullOrWhiteSpace(item))
18            throw new ArgumentException("item is required", nameof(item));
19
20        _items.Add(item);
21    }
22}
23
24public static class Program
25{
26    public static void Main()
27    {
28        var cart = new Cart();
29        cart.Add("Keyboard");
30        cart.Add("Mouse");
31
32        foreach (var item in cart.Items)
33            Console.WriteLine(item);
34
35        Console.WriteLine($"Count via view: {cart.ItemsView.Count}");
36    }
37}

Notice what this does not guarantee: immutability. AsReadOnly blocks mutation through the wrapper, but the underlying list can still change from inside the class.

Live View Versus Snapshot Semantics

This is where teams get bitten in production. A read-only wrapper is often a live view. If internal state changes, observers see new values immediately. Sometimes that is desirable, but many consumers assume they received a snapshot.

If snapshot behavior is required, return a copy or an immutable structure.

csharp
1using System.Collections.Generic;
2using System.Collections.Immutable;
3
4public sealed class AuditBuffer
5{
6    private readonly List<string> _events = new();
7
8    public void Record(string evt) => _events.Add(evt);
9
10    // Immutable snapshot at call time.
11    public ImmutableArray<string> Snapshot()
12    {
13        return _events.ToImmutableArray();
14    }
15}

This has allocation cost, but it gives deterministic point-in-time semantics. For audit and reporting paths, that trade-off is usually worth it.

Why IEnumerable Is Powerful but Easy to Misuse

IEnumerable is intentionally minimal. That flexibility is useful, but it can hide expensive behavior:

  • repeated enumeration may recompute data.
  • deferred execution may run with different results each time.
  • consumers may assume list-like behavior and call extension methods repeatedly.

If your property returns a deferred LINQ chain, document it or materialize it where appropriate. Hidden deferred execution often causes performance incidents that look unrelated to the API surface.

A practical rule is to use method names to signal cost. Returning an expensive deferred query from a property can surprise callers, while a method like GetActiveItems() communicates that work may happen.

Choosing Between the Common Options

In modern .NET code, these contracts usually work best:

  • 'IEnumerable<T> when callers only need iteration.'
  • 'IReadOnlyList<T> when count and indexing matter.'
  • 'ReadOnlyCollection<T> when you need a concrete wrapper type and list semantics.'
  • immutable collections when snapshot and thread-safe sharing are required.

The more your consumers rely on positional access, the less ideal plain IEnumerable<T> becomes. Repeated calls to ElementAt over an unknown enumerable can be accidentally expensive.

Concurrency Considerations

Read-only wrappers do not solve thread safety. If one thread mutates the list while another enumerates a live view, you can still hit race conditions or enumeration exceptions. If concurrent reads are required, prefer immutable snapshots or synchronization around access.

For high-read workloads with periodic updates, publishing immutable snapshots is often the cleanest model. Producers build a new immutable value and replace a reference atomically; readers see a stable view without locks.

Common Pitfalls

  • Returning List<T> directly and letting callers mutate internals.
  • Assuming ReadOnlyCollection<T> means immutable snapshot.
  • Exposing deferred IEnumerable<T> without documenting repeated evaluation cost.
  • Choosing IEnumerable<T> when callers actually need indexed access.
  • Ignoring concurrency and expecting read-only wrappers to prevent race conditions.

Summary

  • Pick collection return types as API contracts, not implementation leaks.
  • Use IEnumerable<T> for iteration-only scenarios.
  • Use index-aware read contracts when consumers need count and position.
  • Distinguish clearly between live views and snapshots.
  • Use immutable snapshots or synchronization when concurrency matters.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.