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
.
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 onlyIEnumerable<T>
is contra-variant [sic1]: InIEnumerable<T>
, the type parameterT
is used only in "output" positions (return values) and not in "input" positions (parameters).ICollection<T>
andIList<T>
useT
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.