2008-12-09

.NET BCL proposition: read-only collection interfaces

As a result of this thread on Channel 9, here's a proposition for a simple enhancement of the .NET BCL, related to collections.

If a class wants to expose a collection of items, the best thing to do is expose the collection as an abstract interface, so the internal class that is used can be changed without changing the API. If you want to expose a read-only collection, the options you have are IEnumerable<T>, ICollection<T> or IList<T>. IEnumerable<T> represents a read-only collection that can be enumerated, but you can't ask it how much elements it has. ICollection<T> and IList<T> represent collections that could be read-only, but this can only be checked at runtime and not defined at compile-time.

My proposition is to add 2 additional interfaces IReadableCollection<T> and IReadableList<T> that contain a subset of the members of ICollection<T> and IList<T> that can be performed on read-only collections. ICollection<T> could then implement IReadableCollection<T> and IList<T> could implement IReadableList<T> (and also ICollection<T>, like it does now). Adding these interfaces would not break existing code, they would be fully backward compatible and would not even require changes to the classes that implement ICollection<T> and IList<T>: they would automatically also implement the new interfaces (because ICollection<T> and IList<T> implement IReadableCollection<T> and IReadableList<T>).

Here's how the interface hierarchy would look:

public interface IEnumerable<T> : IEnumerable
{
  IEnumerator<T> GetEnumerator();
}

public interface IReadableCollection<T> : IEnumerable<T>
{
  int Count { get; }
  bool Contains(T item);
  void CopyTo(T[] array, int arrayIndex);
}

public interface IReadableList<T> : IReadableCollection<T>
{
  T this[int index] { get; }
  int IndexOf(T item);
}

public interface ICollection<T> : IReadableCollection<T>
{
  void Clear();
  void Add(T item);
  bool Remove(T item);
  bool IsReadOnly { get; }
}

public interface IList<T> : ICollection<T>, IReadableList<T>
{
  T this[int index] { get; set; }
  void Insert(int index, T item);
  void RemoveAt(int index);
}