这可能是个老问题:为什么IEnumerable<T>
会继承IEnumerable
?
.NET就是这样做的,但它带来了一些麻烦。每次我编写一个类实现IEumerable<T>
时,我都必须编写两个GetEnumerator()
函数,一个用于IEnumerable<T>
,另一个用于IEnumerable
。
而且,IList<T>
不会从IList继承。
我不知道为什么IEnumerable<T>
是以其他方式设计的。
答案 0 :(得分:49)
Straight from the horse's mouth(Hejlsberg):
理想情况下,所有通用集合接口(例如
ICollection<T>
,IList<T>
)都将从其非通用对应接口继承,以便通用接口实例可以与通用和非通用代码一起使用。例如,如果IList<T>
可以传递给期望IList
的代码,那将会很方便。事实证明,唯一可能的通用接口是
IEnumerable<T>
,因为只有IEnumerable<T>
是反变量的:在IEnumerable<T>
中,类型参数T仅用于在“输出”位置(返回值)而不在“输入”位置(参数)。ICollection<T>
和IList<T>
在输入和输出位置都使用T,因此这些接口是不变的。 (顺便说一句,如果T仅用于输入位置,那么它们将是反对变体,但这在这里并不重要。)&LT; ...剪断...&GT;
因此,为了回答您的问题,IEnumerable<T>
继承自IEnumerable
,因为它可以! : - )
答案 1 :(得分:16)
IEnumerable
的答案是:“因为它可以不影响类型安全”。
IEnumerable
是一个“只读”接口 - 所以通用形式比非通用形式更具体并不重要。你不会通过实现两者来打破任何事情。 IEnumerator.Current
会返回object
,而IEnumerator<T>.Current
会返回T
- 这没关系,因为您可以合法地转换为object
,但这可能意味着装箱。
将此与IList<T>
和IList
进行比较 - 您可以在Add(object)
上调用IList
,而对于任何特定IList<T>
,这可能无效(任何内容)实际上除了IList<object>
之外。
Brad Abram关于这个问题的blogged with Anders' answer。
答案 2 :(得分:3)
这是为了向后兼容。如果你调用一个期望一个vanilla IEnumerable的.Net 1.1函数,你可以传入你的通用IEnumerable。
Luckilly,通用IEnumerator继承自旧式IEnumerator
我通常实现一个返回枚举器的私有方法,然后为旧式和新式GetEnumerator方法传递它。
private IEnumerator<string> Enumerator() {
// ...
}
public IEnumerator<string> GetEnumerator() {
return Enumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return Enumerator();
}
答案 3 :(得分:2)
这样就可以使用不支持泛型的类。此外,.NET泛型不允许您执行诸如强制转换IList&lt; long&gt;之类的操作。作为IList&lt; int&gt;,因此当您需要固定的基类或接口时,非通用版本的接口非常有用。