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
.
It implements IList<TheType> and not IList You need to check the generic type definitions for the interfaces.
Overall, List is a concrete type that implements the IList interface.
You should use IList when you need access by index to your collection, add and delete elements, etc., and IEnumerable when you need just enumerate over your collection.
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.
Note that since 2012, in .NET 4.5 and later, there exists a covariant (out
modifier) interface,
public interface IReadOnlyList<out T>
see its documentation.
Usual collection types like List<YourClass>
, Collection<YourClass>
and YourClass[]
do implement IReadOnlyList<YourClass>
and because of the covariance can also be used as IReadOnlyList<SomeBaseClass>
and ultimately IReadOnlyList<object>
.
As you have guessed, you will not be able to modify your list through a IReadOnlyList<>
reference.
With this new interface, you might be able to avoid the non-generic IList
all together. However you will still have the problem that IReadOnlyList<T>
is not a base interface of IList<T>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With