Search code examples
c#collectionsienumerablereadonly-collection

How to prevent a method caller from modifying a returned collection?


I have methods returning private collections to the caller and I want to prevent the caller from modifying the returned collections.

private readonly Foo[] foos;

public IEnumerable<Foo> GetFoos()
{
    return this.foos;
}

At the moment the private collection is a fixed array, but in the future the collection might become a list if the need for adding new items at run time arises.

There are several solutions to prevent the caller from modifying the collection. Returning IEnumerable<T> is the simplest solution, but the caller can still up-cast the return value to IList<T> and modify the collection.

((IList<Foo>)GetFoos())[0] = otherFoo;

Cloning the collections has the obvious disadvantage that there are two collections that can evolve independently. So far I have considered the following options.

  1. Wrapping the collection in ReadOnlyCollection<T>.
  2. Returning one of the LINQ iterators defined by the Enumerable class by performing a dummy projection like list.Select(item => item). Actually I consider using Where(item => true) because the returned iterator seems more lightweight.
  3. Writing a custom wrapper.

What I don't like about using ReadOnlyCollection<T> is that it implements IList<T> and calling Add() or accessing the indexer will cause exceptions. While this is absolutly correct in theory, almost no real code checks IList<T>.IsReadOnly or IList<T>.IsFixedSize.

Using the LINQ iterators - I wrapped the code in an extension method MakeReadOnly() - prevents this scenario, but it has the taste of a hack.

Writing a custom wrapper? Reinventing the wheel?

Any thoughts, considerations, or other solutions?


While tagging this question, I discovered this Stack Overflow question I didn't notice before. Jon Skeet suggest to use the "LINQ hack", too, but even more efficient using Skip(0).


Solution

  • Unfortunately there is no way to achieve exactly what you are looking for in the current version of the framework. It simply has no concept of an indexable immutable / read-only collection on both the concrete type and interface style.

    As you pointed out, ReadOnlyCollection<T> works OK on the concrete type side. But there is no corresponding interface to implement which is also statically read-only.

    You're only real choice is to ...

    • Define your own collection class
    • Either only implement IEnumerable<T> or define a need read-only interface which your collection implements.