简体   繁体   English

为什么泛型IList <>不继承非泛型IList

[英]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 . 当你注意, TIList<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>不会扩展ICollectionIList<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 only IEnumerable<T> is contra-variant [sic 1 ] : In IEnumerable<T> , the type parameter T 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> and IList<T> use T in both input and output positions, and those interfaces are therefore invariant. ICollection<T>IList<T>在输入和输出位置都使用T ,因此这些接口是不变的。

1 ) IEnumerable<T> is co -variant 1IEnumerable<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] . 坦率地说,我发现它们不是很有用,因为它们只定义了Countthis[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> ,它ContainsIndexOf ,以及一个可变的不变接口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.

相关问题 从IList转换 <T> 到非通用的IList - Convert from IList<T> to non-generic IList 如何将非通用IList转换为IList <T> ? - How do I convert a non-Generic IList to IList<T>? 为什么.Net FCL正在实现通用和非通用接口IList和IList <T> 在列表中 <T> 同时上课? - Why .Net FCL is implementing generic and non-generic interface IList and IList<T> in List<T> class at same time? Avro.AvroException:数组未在字段中实现非泛型 IList - Avro.AvroException: Array does not implement non-generic IList in field 在特殊情况下解决通用和非通用IList扩展方法之间的歧义调用 - Resolve ambiguous calls between a generic and a non-generic IList extension method in special circumstances 为什么要使用通用ICollection <T> 不继承一些带有Count属性的非通用接口? - Why generic ICollection<T> does not inherit some non-generic interface with Count property? 为什么 IEnumerator<T> 继承自 IDisposable 而非泛型 IEnumerator 不? - Why does IEnumerator<T> inherit from IDisposable while the non-generic IEnumerator does not? 为什么IList和ICollection的通用对应项没有相同的方法集? - Why does not the generic counterparts of IList and ICollection have the same set of methods? 为什么存在通用和非通用IComparable接口? - Why does a generic and a non-generic IComparable interface exist? .NET IList &lt;&gt;(通用IList接口)是过时的还是不实用的? - Is the .NET IList<> (generic IList interface) obsolete or not practical?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM