Why generic IList<> does not inherit non-generic IList

.net, c#, generics, library-design

Solution

As you note, `T` in `IList<T>` is not covariant. As a rule of thumb: any class that can modify its state cannot be covariant. The reason is that such classes often have methods that have `T` as the type of one of their parameters, e.g. `void Add(T element)`. And covariant type parameters are not allowed in input positions.

Generics were added, among other reasons, to provide type safety. For example, you can't add an `Elephant` to a list of `Apple`. If `ICollection<T>` were to extend `ICollection`, then you could call `((ICollection)myApples).Add(someElephant)` without a compile-time error, as `ICollection` has a method `void Add(object obj)`, which seemingly allows you to add any object to the list, while in practice you can only add objects of `T`. Therefore, `ICollection<T>` does not extend `ICollection` and `IList<T>` does not extend `IList`.

Anders Hejlsberg, one of the creators of C#, explains it like this:

Ideally all of the generic collection interfaces (e.g. `ICollection<T>`, `IList<T>`) would inherit from their non-generic counterparts such that generic interface instances could be used both with generic and non-generic code.

As it turns out, the only generic interface for which this is possible is `IEnumerable<T>`, because only `IEnumerable<T>` is contra-variant [sic1]: In `IEnumerable<T>`, the type parameter `T` is used only in "output" positions (return values) and not in "input" positions (parameters). `ICollection<T>` and `IList<T>` use `T` in both input and output positions, and those interfaces are therefore invariant.

1) `IEnumerable<T>` is co-variant

Since .Net 4.5 there are the `IReadOnlyCollection<out T>` and `IReadOnlyList<out T>` covariant interfaces. But `IList<T>`, `ICollection<T>` and many of the list and collection classes don't implement or extend them. Frankly, I find them not very useful, as they only define `Count` and `this[int index]`.

If I could redesign .Net 4.5 from the ground up, I would have split the list interface into a read-only covariant interface `IList<out T>` that includes `Contains` and `IndexOf`, and a mutable invariant interface `IMutableList<T>`. Then you could cast `IList<Apple>` to `IList<object>`. I implemented this here:

M42 Collections - Covariant collections, lists and arrays.

Problem

`IList<T>` does not inherit `IList` where `IEnumerable<out T>` inherits `IEnumerable`. If `out` modifier are the only reason then why most of the implementation of `IList<T>` (e.g. `Collection<T>`, `List<T>`) implements `IList` interface. So any one can say OK, if that statements is true for all implementation of `IList<T>` then directly cast it to `IList` when necessary. But problem is that though `IList<T>` does not inherit `IList` so it is not guaranteed that every `IList<T>` object are `IList`. Moreover using `IList<object>` is obviously not the solution because without `out` modifier generics can not be assigned to a less inherit class; and creating new instance of List is not a solution here because someone may want actual reference of the `IList<T>` as an `IList` pointer; and use `List<T>` insteed of `IList<T>` is actually a bad programming practice and doesn't serve all purpose. If .NET wants to give flexibility that every implementation of `IList<T>` should not have a contract of non-generic implementation (i.e. `IList`) then why they didn't keep another interface which implement both generic and non-generic version and didn't suggest that all concrete class which want to contract for generic and non-genetic item should contract via that interface. Same problem occurs for casting `ICollection<T>` to `ICollection` and `IDictionary<TKey, TValue>` to `IDictionary`.

Original source

Related problems