[英]Why generic IList<> does not inherit non-generic IList
IList<T>
does not inherit IList
where IEnumerable<out T>
inherits IEnumerable
. IList<T>
不继承IList
,其中IEnumerable<out T>
继承IEnumerable
。
If out
modifier are the only reason then why most of the implementation of IList<T>
(eg Collection<T>
, List<T>
) implements IList
interface. 如果
out
修饰符是唯一的原因,那么为什么大多数IList<T>
的实现(例如Collection<T>
, List<T>
)都实现了IList
接口。
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. 所以任何人都可以说好,如果对于
IList<T>
所有实现都是如此,那么在必要时将其直接转换为IList
。 But problem is that though IList<T>
does not inherit IList
so it is not guaranteed that every IList<T>
object are IList
. 但问题是虽然
IList<T>
不继承IList
因此无法保证每个IList<T>
对象都是IList
。
Moreover using IList<object>
is obviously not the solution because without out
modifier generics can not be assigned to a less inherit class; 而且使用
IList<object>
显然不是解决方案,因为没有out
修饰符泛型不能分配给较少的继承类; 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; 并且创建List的新实例不是解决方案,因为有人可能想要
IList<T>
实际引用作为IList
指针; and use List<T>
insteed of IList<T>
is actually a bad programming practice and doesn't serve all purpose. 并使用
List<T>
insured IList<T>
实际上是一个糟糕的编程实践,并不适用于所有目的。
If .NET wants to give flexibility that every implementation of IList<T>
should not have a contract of non-generic implementation (ie 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. 如果.NET想要提供灵活性,
IList<T>
每个实现都不应该有非泛型实现的合同(即IList
)那么为什么他们没有保留另一个实现泛型和非泛型版本的接口并且没有建议所有希望签订通用和非遗传项目的具体类别应通过该接口签订合同。
Same problem occurs for casting ICollection<T>
to ICollection
and IDictionary<TKey, TValue>
to IDictionary
. 将
ICollection<T>
为ICollection
并将IDictionary<TKey, TValue>
为IDictionary
时也会出现同样的问题。
As you note, T
in IList<T>
is not covariant . 当你注意,
T
中IList<T>
不是协变的 。 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, eg void Add(T element)
. 原因是这些类通常具有将
T
作为其参数之一的类型的方法,例如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
. 例如,您无法将
Elephant
添加到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
. 如果
ICollection<T>
要扩展ICollection
,那么你可以调用((ICollection)myApples).Add(someElephant)
而没有编译时错误,因为ICollection
有一个方法void Add(object obj)
,它似乎允许你添加列表中的任何对象,而在实践中,您只能添加T
对象。 Therefore, ICollection<T>
does not extend ICollection
and IList<T>
does not extend IList
. 因此,
ICollection<T>
不会扩展ICollection
, IList<T>
不会扩展IList
。
Anders Hejlsberg, one of the creators of C#, explains it like this : C#的创造者之一Anders Hejlsberg 解释如下 :
Ideally all of the generic collection interfaces (eg
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.理想情况下,所有通用集合接口(例如
ICollection<T>
,IList<T>
)将从其非通用对应物继承,使得通用接口实例可以与通用和非通用代码一起使用。As it turns out, the only generic interface for which this is possible is
IEnumerable<T>
, because onlyIEnumerable<T>
is contra-variant [sic 1 ] : InIEnumerable<T>
, the type parameterT
is used only in "output" positions (return values) and not in "input" positions (parameters).事实证明,唯一可能的通用接口是
IEnumerable<T>
,因为只有IEnumerable<T>
是反变量[sic 1 ] :在IEnumerable<T>
,类型参数T
仅用于“输出“位置(返回值)而不是”输入“位置(参数)。ICollection<T>
andIList<T>
useT
in both input and output positions, and those interfaces are therefore invariant.ICollection<T>
和IList<T>
在输入和输出位置都使用T
,因此这些接口是不变的。
1 ) IEnumerable<T>
is co -variant 1 )
IEnumerable<T>
是共变的
Since .Net 4.5 there are the IReadOnlyCollection<out T>
and IReadOnlyList<out T>
covariant interfaces. 从.Net 4.5开始,有
IReadOnlyCollection<out T>
和IReadOnlyList<out T>
协变接口。 But IList<T>
, ICollection<T>
and many of the list and collection classes don't implement or extend them. 但
IList<T>
, ICollection<T>
以及许多列表和集合类都没有实现或扩展它们。 Frankly, I find them not very useful, as they only define Count
and this[int index]
. 坦率地说,我发现它们不是很有用,因为它们只定义了
Count
和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>
. 如果我可以从头开始重新设计.Net 4.5,我会将列表接口拆分为一个只读的协变接口
IList<out T>
,它Contains
和IndexOf
,以及一个可变的不变接口IMutableList<T>
。 Then you could cast IList<Apple>
to IList<object>
. 然后你可以将
IList<Apple>
为IList<object>
。 I implemented this here: 我在这里实现了这个:
M42 Collections - Covariant collections, lists and arrays.
M42集合 - 协变集合,列表和数组。
Note that since 2012, in .NET 4.5 and later, there exists a covariant ( out
modifier) interface, 请注意,自2012年以来,在.NET 4.5及更高版本中,存在协变(
out
修饰符)接口,
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>
. 通常的集合类型如
List<YourClass>
, Collection<YourClass>
和YourClass[]
确实实现了IReadOnlyList<YourClass>
并且由于协方差也可以用作IReadOnlyList<SomeBaseClass>
并最终用作IReadOnlyList<object>
。
As you have guessed, you will not be able to modify your list through a IReadOnlyList<>
reference. 正如您所猜测的那样,您将无法通过
IReadOnlyList<>
引用修改列表。
With this new interface, you might be able to avoid the non-generic IList
all together. 使用这个新界面,您可以一起避免使用非通用
IList
。 However you will still have the problem that IReadOnlyList<T>
is not a base interface of IList<T>
. 但是,您仍然会遇到
IReadOnlyList<T>
不是IList<T>
的基接口的问题。
Create an interface MyIList<T>
and let it inherit from IList<T>
and IList
: 创建一个接口
MyIList<T>
并让它继承IList<T>
和IList
:
public interface MyIList<T> : IList<T>, IList
{ }
Now create a class MySimpleList
and let it implement MyIList<T>
: 现在创建一个
MySimpleList
类,让它实现MyIList<T>
:
public class MySimpleList<T> : MyIList<T>
{
public int Count
{
get { throw new NotImplementedException(); }
}
public bool IsFixedSize
{
get { throw new NotImplementedException(); }
}
public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}
public bool IsSynchronized
{
get { throw new NotImplementedException(); }
}
public object SyncRoot
{
get { throw new NotImplementedException(); }
}
object IList.this[int index]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public T this[int index]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public void Add(T item)
{
throw new NotImplementedException();
}
public int Add(object value)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(T item)
{
throw new NotImplementedException();
}
public bool Contains(object value)
{
throw new NotImplementedException();
}
public void CopyTo(T[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public void CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
public IEnumerator<T> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
public int IndexOf(T item)
{
throw new NotImplementedException();
}
public int IndexOf(object value)
{
throw new NotImplementedException();
}
public void Insert(int index, T item)
{
throw new NotImplementedException();
}
public void Insert(int index, object value)
{
throw new NotImplementedException();
}
public bool Remove(T item)
{
throw new NotImplementedException();
}
public void Remove(object value)
{
throw new NotImplementedException();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
}
What you can easily see now, is that you have to double implement a bunch of methods. 您现在可以轻松看到的是,您必须双重实现一堆方法。 One for the type T and one for object.
一个用于T型,一个用于物体。 In normal circumstances you want to avoid this.
在正常情况下,你想避免这种情况。 This is a problem of co-variance and contra-variance.
这是共方差和反方差的问题。
The best explanation you can find (for this concrete problem with IList and IList is the article from Brad already mentioned by Jon within the comments of the question. 您可以找到的最佳解释(对于IList和IList的这个具体问题,是Jon在问题的评论中已经提到的Brad的文章 。
Good answers have already been given. 已经给出了很好的答案。 A notice about IList though:
关于IList的通知:
MSDN IList Remarks : "IList implementations fall into three categories: read-only, fixed-size, and variable-size. (...). For the generic version of this interface, see System.Collections.Generic.IList<T>
." MSDN IList备注 :“IList实现分为三类:只读,固定大小和可变大小。(...)。对于此接口的通用版本,请参阅
System.Collections.Generic.IList<T>
“。
This is a bit misleading because on the generic side, we have IList<T>
as variable-size , and IReadOnlyList<T>
as read-only since 4.5 but AFAIK, there is no fixed-size generic List. 这有点误导,因为在通用方面,我们将
IList<T>
作为变量大小 ,并且IReadOnlyList<T>
作为只读,因为4.5但是AFAIK,没有固定大小的通用List。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.