为什么通用 IList<>不继承非泛型 IList [英] Why generic IList<> does not inherit non-generic IList
问题描述
IList
不继承 IList
其中 IEnumerable
继承 IEnumerable
.>
如果out
修饰符是唯一的原因,那么为什么大多数IList
的实现(例如Collection
,List
) 实现了 IList
接口.
所以任何人都可以说好的,如果该语句对于 IList
的所有实现都是正确的,那么在必要时直接将其转换为 IList
.但问题是虽然 IList
不继承 IList
所以不能保证每个 IList
对象都是 IList
.
此外,使用 IList
显然不是解决方案,因为没有 out
修饰符泛型不能分配给较少继承的类;并且在这里创建 List 的新实例不是解决方案,因为有人可能希望将 IList
实际引用作为 IList
指针;并使用 List
代替 IList
实际上是一种糟糕的编程习惯,并不能达到所有目的.
如果 .NET 想要给 IList
的每个实现提供灵活性,不应该有非通用实现的契约(即 IList
),那么为什么他们没有不保留另一个实现泛型和非泛型版本的接口,也没有建议所有想要为泛型和非遗传项签约的具体类都应该通过该接口签约.
将 ICollection
转换为 ICollection
并将 IDictionary
转换为 IDictionary
也会出现同样的问题>.
如您所见,IList
中的 T
不是 协变.根据经验:任何可以修改其状态的类都不能是协变的.原因是这些类通常具有将 T
作为其参数之一的类型的方法,例如void Add(T element)
.输入位置不允许使用协变类型参数.
添加泛型,除其他原因外,是为了提供类型安全.例如,您不能将 Elephant
添加到 Apple
的列表中.如果 ICollection
要扩展 ICollection
,那么您可以调用 ((ICollection)myApples).Add(someElephant)
而无需编译-时间错误,因为ICollection
有一个方法void Add(object obj)
,它似乎允许您将任何 对象添加到列表中,而在实践中你只能添加T
的对象.因此,ICollection
不会扩展 ICollection
并且 IList
不会扩展 IList
.>
Anders Hejlsberg,C# 的创造者之一,这样解释:
<块引用>理想情况下,所有泛型集合接口(例如 ICollection
、IList
)都将从它们的非泛型对应物继承,以便泛型接口实例可以可用于泛型和非泛型代码.
事实证明,唯一可能的通用接口是 IEnumerable
,因为只有 IEnumerable
是逆变的[sic1]:在IEnumerable
中,类型参数T
仅用于输出"位置(返回值)而不是输入"位置(参数).ICollection
和 IList
在输入和输出位置都使用 T
,因此这些接口是不变的.
1) IEnumerable
是 co-variant
从 .Net 4.5 开始,有 IReadOnlyCollection
和 IReadOnlyList
协变接口.但是 IList
、ICollection
和许多列表和集合类没有实现或扩展它们.坦率地说,我发现它们不是很有用,因为它们只定义了 Count
和 this[int index]
.
如果我可以从头开始重新设计 .Net 4.5,我会将列表接口拆分为只读协变接口 IList
,其中包括 Contains
和 IndexOf
,以及一个可变的不变接口 IMutableList
.然后你可以将 IList
转换为 IList
.我在这里实现了这个:
M42 集合 - 协变集合、列表和数组.
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.
这篇关于为什么通用 IList<>不继承非泛型 IList的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!