Searching for a read-only list

A while back, Eric Lippert talked about arrays being somewhat harmful.  The reason being, if you’re looking to create logical immutability, arrays aren’t it.  Array length is immutable, but its contents aren’t.  If you need to return an array, make sure you create a new instance every single time, or it might get modified underneath you.

Eric discusses in detail the issues with arrays, and all of his issues make sense.  The only problem is, there aren’t any good alternatives in the BCL for a read-only collection.

You can use IEnumerable<T>, but all of its functionality comes from the LINQ query extensions.  Count(), which was Length for arrays, is O(n) for most cases.  It does exactly what you think it does, and loops through the entire list and increments a count.  It does some intelligent checking, and returns the Count property for ICollection<T> implementations, but for all other cases, just loops through the list.

IEnumerable<T> also does not provide an indexer, so that really won’t work.

How about the next on the list, ICollection<T>?  Here’s that interface:

public interface ICollection<T> : IEnumerable<T>, IEnumerable
    // Methods
    void Add(T item);
    void Clear();
    bool Contains(T item);
    void CopyTo(T[] array, int arrayIndex);
    bool Remove(T item);

    // Properties
    int Count { get; }
    bool IsReadOnly { get; }

Hmmm, not quite.  There is an Add method, but no indexer.  And a funny flag, “IsReadOnly”, that users of the collection are supposed to check?  That’s not a very good user experience.  Also not good, as implementers of ICollection<T> are expected to throw a NotSupportedException if IsReadOnly is false, for mutating operations like Add and Clear.

Blech, another violation of the Interface Segregation Principle.  If I have a read-only list, why would I need to implement any read-only operations?

Looking further, we find a promising candidate: ReadOnlyCollection<T>.  However, this class merely wraps another list, which can be modified underneath.  Additionally, this class implements IList and IList<T>, which means you could potentially get runtime errors because Add and Clear look like available operations.

The final option is an IReadOnlyList<T>, of which there are a few options floating around the interwebs.  It would look something like this:

public interface IReadOnlyList<T> : IEnumerable<T>
    int IndexOf(T item);
    bool Contains(T item);
    void CopyTo(T[] array, int arrayIndex);

    int Count { get; }
    T this[int index] { get; }

It’s not an ideal solution, but if you want immutable size and immutable contents, this kind of direction will be the way to go.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in C#. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • When you say “immutable contents” I think you mean that objects cannot be added or removed from the collection. But when you say “immutable contents” it sounds like this would mean that objects contained in the list cannot be modified. I’m fairly certain you intend the former rather than the latter.

    –Jeff Doolittle

  • @Jeff

    Yes, the former.

    int[] vals = {1, 2, 3, 4};
    vals[2] = 584;

    The length is immutable, but the you can change what items are contained in the array.

  • Good post, I definitely agree and have come to the same conclusion:

    I really think MS dropped the ball on collections and their interfaces, oh well.

  • Utterly agree. One of the things that annoyed me when moving from C++ to C# all those years ago was lack of const correctness and after 4 releases they’ve still not got it right! As an object I want to be able to return references to my internal state and ensure that the client isn’t able to modify me. You can do it with your own objects but not with in built in types.

  • test


  • Konstantin Spirin

    Neil, C++ is not 100% good and you can modify even constant objects – just typecast them.

    Jimmy, maybe it is better to call your interface “IReadableList” so it will be fine to implement it in class “List” (otherwise it looks weird if your list implements IReadOnlyList and at the same time is modifyable).