正如你所注意到的,T
in IList<T>
is not 协变。根据经验:任何可以修改其状态的类都不能是协变的。原因是此类类通常具有以下方法:T
作为其参数之一的类型,例如void Add(T element)
。并且不允许使用协变类型参数input职位。
添加泛型的原因之一是为了提供类型安全。例如,您不能添加Elephant
到一个列表Apple
. If ICollection<T>
是要延长ICollection
,然后你可以打电话((ICollection)myApples).Add(someElephant)
没有编译时错误,如ICollection
有一个方法void Add(object obj)
,这似乎允许您添加any对象到列表中,而实际上您只能添加对象T
。所以,ICollection<T>
不延长ICollection
and IList<T>
不延长IList
.
Anders Hejlsberg,C# 的创始人之一,像这样解释它 http://blogs.msdn.com/b/brada/archive/2005/01/18/355755.aspx:
理想情况下所有通用集合接口(例如ICollection<T>
, IList<T>
)将从其非泛型对应项继承,以便泛型接口实例可以与泛型和非泛型代码一起使用。
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
从 .Net 4.5 开始,有IReadOnlyCollection<out T> http://msdn.microsoft.com/en-us/library/hh881542.aspx and IReadOnlyList<out T> http://msdn.microsoft.com/en-us/library/hh192385.aspx协变接口。但IList<T>
, ICollection<T>
许多列表和集合类没有实现或扩展它们。坦率地说,我发现它们不是很有用,因为它们只定义了Count
and this[int index]
.
如果我可以从头开始重新设计 .Net 4.5,我会将列表接口拆分为只读协变接口IList<out T>
包括Contains
and IndexOf
,以及可变不变接口IMutableList<T>
。然后你就可以施放IList<Apple>
to IList<object>
。我在这里实现了这个:
M42系列 https://bitbucket.org/Virtlink/m42-collections/- 协变集合、列表和数组。