为什么通用 IList<>不继承非泛型 IList [英] Why generic IList<> does not inherit non-generic IList

查看:30
本文介绍了为什么通用 IList<>不继承非泛型 IList的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

IList 不继承 IList 其中 IEnumerable 继承 IEnumerable.>

如果out修饰符是唯一的原因,那么为什么大多数IList的实现(例如CollectionList) 实现了 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# 的创造者之一,这样解释:

<块引用>

理想情况下,所有泛型集合接口(例如 ICollectionIList)都将从它们的非泛型对应物继承,以便泛型接口实例可以可用于泛型和非泛型代码.

事实证明,唯一可能的通用接口是 IEnumerable,因为只有 IEnumerable 是逆变的[sic1]:在IEnumerable中,类型参数T仅用于输出"位置(返回值)而不是输入"位置(参数).ICollectionIList 在输入和输出位置都使用 T,因此这些接口是不变的.

1) IEnumerableco-variant

<小时>

从 .Net 4.5 开始,有 IReadOnlyCollectionIReadOnlyList 协变接口.但是 IListICollection 和许多列表和集合类没有实现或扩展它们.坦率地说,我发现它们不是很有用,因为它们只定义了 Countthis[int index].

<小时>

如果我可以从头开始重新设计 .Net 4.5,我会将列表接口拆分为只读协变接口 IList,其中包括 ContainsIndexOf,以及一个可变的不变接口 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 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.

这篇关于为什么通用 IList&lt;&gt;不继承非泛型 IList的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆