16

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);
}
Linus Caldwell
  • 10,908
  • 12
  • 46
  • 58
Ashley John
  • 2,379
  • 2
  • 21
  • 36

2 Answers2

20

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.

dlev
  • 48,024
  • 5
  • 125
  • 132
4

The key here is whether the collection is modifiable. IEnumerable<T> is a read-only collection of Ts, whereas ICollection<T> supports Add. A modifiable collection cannot be covariant, because:

IList<String> obj = new List<String>();
ICollection<Object> obj1 = obj;
obj1.Add(new Elephant());

This would typecheck, since (presumably) Elephant is a subclass of Object. But now obj, which is a List<string> has an Elephant as its last element, which is clearly a bad thing.

luqui
  • 59,485
  • 12
  • 145
  • 204