Covariance and IList

Update: from .NET 4.5 onwards there is IReadOnlyList<out T> and IReadOnlyCollection<out T> which are both covariant; The latter is basically IEnumerable<out T> plus Count; the former adds T this[int index] {get;}. It should also be noted that IEnumerable<out T> is covariant from .NET 4.0 onwards.

Both List<T> and ReadOnlyCollection<T> (via List<T>.AsReadOnly()) implement both of these.


It can only be covariant if it only has a get indexer, i.e.

public T this[int index] { get; }

But all main collections have {get;set;}, which makes that awkward. I’m not aware of any that would suffice there, but you could wrap it, i.e. write an extension method:

var covariant = list.AsCovariant();

which is a wrapper around an IList<T> that only exposes the IEnumerable<T> and the get indexer…? should be only a few minutes work…

public static class Covariance
{
    public static IIndexedEnumerable<T> AsCovariant<T>(this IList<T> tail)
    {
        return new CovariantList<T>(tail);
    }
    private class CovariantList<T> : IIndexedEnumerable<T>
    {
        private readonly IList<T> tail;
        public CovariantList(IList<T> tail)
        {
            this.tail = tail;
        }
        public T this[int index] { get { return tail[index]; } }
        public IEnumerator<T> GetEnumerator() { return tail.GetEnumerator();}
        IEnumerator IEnumerable.GetEnumerator() { return tail.GetEnumerator(); }
        public int Count { get { return tail.Count; } }
    }
}
public interface IIndexedEnumerable<out T> : IEnumerable<T>
{
    T this[int index] { get; }
    int Count { get; }
}

Leave a Comment