繁体   English   中英

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

[英]Why generic IList<> does not inherit non-generic IList

IList<T>不继承IList ,其中IEnumerable<out T>继承IEnumerable

如果out修饰符是唯一的原因,那么为什么大多数IList<T>的实现(例如Collection<T>List<T> )都实现了IList接口。

所以任何人都可以说好,如果对于IList<T>所有实现都是如此,那么在必要时将其直接转换为IList 但问题是虽然IList<T>不继承IList因此无法保证每个IList<T>对象都是IList

而且使用IList<object>显然不是解决方案,因为没有out修饰符泛型不能分配给较少的继承类; 并且创建List的新实例不是解决方案,因为有人可能想要IList<T>实际引用作为IList指针; 并使用List<T> insured IList<T>实际上是一个糟糕的编程实践,并不适用于所有目的。

如果.NET想要提供灵活性, IList<T>每个实现都不应该有非泛型实现的合同(即IList )那么为什么他们没有保留另一个实现泛型和非泛型版本的接口并且没有建议所有希望签订通用和非遗传项目的具体类别应通过该接口签订合同。

ICollection<T>ICollection并将IDictionary<TKey, TValue>IDictionary时也会出现同样的问题。

当你注意, TIList<T>不是协变的 根据经验:任何可以修改其状态的类都不能协变。 原因是这些类通常具有将T作为其参数之一的类型的方法,例如void Add(T element) 输入位置不允许使用协变类型参数。

除其他原因外,还增加了仿制药,以提供类型安全性。 例如,您无法将Elephant添加到Apple列表中。 如果ICollection<T>要扩展ICollection ,那么你可以调用((ICollection)myApples).Add(someElephant)而没有编译时错误,因为ICollection有一个方法void Add(object obj) ,它似乎允许你添加列表中的任何对象,而在实践中,您只能添加T对象。 因此, ICollection<T>不会扩展ICollectionIList<T>不会扩展IList

C#的创造者之一Anders Hejlsberg 解释如下

理想情况下,所有通用集合接口(例如ICollection<T>IList<T> )将从其非通用对应物继承,使得通用接口实例可以与通用和非通用代码一起使用。

事实证明,唯一可能的通用接口是IEnumerable<T> ,因为只有IEnumerable<T>是反变量[sic 1 ] :在IEnumerable<T> ,类型参数T仅用于“输出“位置(返回值)而不是”输入“位置(参数)。 ICollection<T>IList<T>在输入和输出位置都使用T ,因此这些接口是不变的。

1IEnumerable<T>变的


从.Net 4.5开始,有IReadOnlyCollection<out T>IReadOnlyList<out T>协变接口。 IList<T>ICollection<T>以及许多列表和集合类都没有实现或扩展它们。 坦率地说,我发现它们不是很有用,因为它们只定义了Countthis[int index]


如果我可以从头开始重新设计.Net 4.5,我会将列表接口拆分为一个只读的协变接口IList<out T> ,它ContainsIndexOf ,以及一个可变的不变接口IMutableList<T> 然后你可以将IList<Apple>IList<object> 我在这里实现了这个:

M42集合 - 协变集合,列表和数组。

请注意,自2012年以来,在.NET 4.5及更高版本中,存在协变( out修饰符)接口,

public interface IReadOnlyList<out T>

它的文件

通常的集合类型如List<YourClass>Collection<YourClass>YourClass[]确实实现了IReadOnlyList<YourClass>并且由于协方差也可以用作IReadOnlyList<SomeBaseClass>并最终用作IReadOnlyList<object>

正如您所猜测的那样,您将无法通过IReadOnlyList<>引用修改列表。

使用这个新界面,您可以一起避免使用非通用IList 但是,您仍然会遇到IReadOnlyList<T>不是IList<T>的基接口的问题。

创建一个接口MyIList<T>并让它继承IList<T>IList

public interface MyIList<T> : IList<T>, IList
{ }

现在创建一个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();
    }
}

您现在可以轻松看到的是,您必须双重实现一堆方法。 一个用于T型,一个用于物体。 在正常情况下,你想避免这种情况。 这是共方差和反方差的问题。

您可以找到的最佳解释(对于IList和IList的这个具体问题,是Jon在问题的评论中已经提到的Brad文章

已经给出了很好的答案。 关于IList的通知:

MSDN IList备注 :“IList实现分为三类:只读,固定大小和可变大小。(...)。对于此接口的通用版本,请参阅System.Collections.Generic.IList<T> “。

这有点误导,因为在通用方面,我们将IList<T>作为变量大小 ,并且IReadOnlyList<T>作为只读,因为4.5但是AFAIK,没有固定大小的通用List。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM