一尘不染

协方差和IList

c#

我想要一个可以通过索引检索其项目的Covariant集合。IEnumerable是我所知道的唯一的.net集合是Covariant,但是它没有此索引支持。

具体来说,我想这样做:

List<Dog> dogs = new List<Dog>();

IEnumerable<Animal> animals = dogs;
IList<Animal> animalList = dogs; // This line does not compile

现在,我知道了为什么这是一个问题。列出ICollection具有Add方法的实现。通过强制转换IList为Animals,它将允许后续代码添加“真实”
List<Dog>集合中不允许的任何类型的动物。

那么,有谁知道一个集合也支持协变量的索引查找?我不想创建自己的。


阅读 223

收藏
2020-05-19

共1个答案

一尘不染

更新:从.NET 4.5开始IReadOnlyList<out T>IReadOnlyCollection<out T>这两者都是协变的;后者基本上是IEnumerable<out T>加号Count; 前者补充说T this[int index] {get;}。还应注意,IEnumerable<out T>从.NET
4.0开始是协变的。

双方List<T>ReadOnlyCollection<T>(通过List<T>.AsReadOnly())实现这两个。


如果只有get索引器,则只能是协变的,即

public T this[int index] { get; }

但是所有主要收藏都有{get;set;},这很尴尬。我不知道有什么可以满足要求的,但是您可以 包装 起来,即编写一个扩展方法:

var covariant = list.AsCovariant();

这是一个包装器IList<T>,仅暴露IEnumerable<T>和和get索引器…?应该只有几分钟的工作…

public static class Covariance
{
    public static IIndexedEnumerable<T> AsCovariant<T>(this IList<T> tail)
    {
        return new CovariantList<T>(tail);
    }
    private class CovariantList<T> : IIndexedEnumerable<T>
    {
        private readonly IList<T> tail;
        public CovariantList(IList<T> tail)
        {
            this.tail = tail;
        }
        public T this[int index] { get { return tail[index]; } }
        public IEnumerator<T> GetEnumerator() { return tail.GetEnumerator();}
        IEnumerator IEnumerable.GetEnumerator() { return tail.GetEnumerator(); }
        public int Count { get { return tail.Count; } }
    }
}
public interface IIndexedEnumerable<out T> : IEnumerable<T>
{
    T this[int index] { get; }
    int Count { get; }
}
2020-05-19