Search code examples
c#genericscovariancecontravariance

Generic Covariance and contravariance


Consider the code snippet.

IList<String> obj=new List<string>();
IEnumerable<Object> obj1 = obj;

But if i write ICollection<Object> obj2 = obj; it throws me a compile time error.

Cannot implicitly convert type 'System.Collections.Generic.IList<string>' to 'System.Collections.Generic.ICollection<object>'.

Why is this behavior since List<T> implements both IEnumerable<T> and ICollection<T> and also IList<T> is defined as

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

Solution

  • ICollection<T> is not covariant on the type parameter, whereas IEnumerable<T> is. If you look at their declarations (ICollection, IEnumerable) you can see that IEnumerable<T> uses the out keyword on T, while ICollection<T> does not.

    This makes sense if you think about it, since (roughly speaking) covariance is safe when the interface will only be used to read objects (and thus the out keyword). IEnumerable<T> clearly meets that criterion, whereas ICollection<T> is quite the opposite.

    As an example of what could go wrong (using your example):

    IList<String> obj = new List<string>(); // Legal, of course
    ICollection<Object> obj1 = obj;         // Illegal, but let's see what happens
    obj1.Add(new NonStringObject());        // That's not a string being stored in a List<string>
    

    Remember: covariance is not the same as inheritance. Just because two classes or interfaces share an inheritance relation does not mean their type parameters share the same variance characteristics.