协方差和IList

Bri*_*n M 47 .net c# .net-4.0 covariance

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

具体来说,我想这样做:

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

IEnumerable<Animal> animals = dogs;
IList<Animal> animalList = dogs; // This line does not compile
Run Code Online (Sandbox Code Playgroud)

现在,我知道为什么这是一个问题.List实现ICollection具有Add方法.通过向上投射IList动物,它将允许后续代码添加"真实" List<Dog>集合中不允许的任何类型的动物.

那么有人知道一个支持索引查找的集合也是协变的吗?我想不创造自己的.

Mar*_*ell 54

更新:从.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; }
Run Code Online (Sandbox Code Playgroud)

但是所有的主要收藏都有{get;set;},这让人感到尴尬.我不知道那里有任何足够的东西,但你可以包装它,即写一个扩展方法:

var covariant = list.AsCovariant();
Run Code Online (Sandbox Code Playgroud)

这是一个包装器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; }
}
Run Code Online (Sandbox Code Playgroud)

  • 我有时希望微软设法让IList继承子接口IReadableByIndex(你称之为IIndexedEnumerable),IWritableByIndex和iAppendable,以便允许有用的协方差和逆变.不幸的是,目前还没有办法通过读写属性实现只读或只写属性.如果可以做到这一点,协变/逆变子接口的所有成员自然会由IList的任何有效实现来实现,因此可以在不破坏现有代码的情况下添加子接口. (2认同)

G-M*_*Mac 7

这是我为解决这个问题而编写的一个类:

public class CovariantIListAdapter<TBase, TDerived> : IList<TBase>
    where TDerived : TBase
{
    private IList<TDerived> source;

    public CovariantIListAdapter(IList<TDerived> source)
    {
        this.source = source;
    }

    public IEnumerator<TBase> GetEnumerator()
    {
        foreach (var item in source)
            yield return item;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void Add(TBase item)
    {
        source.Add((TDerived) item);
    }

    public void Clear()
    {
        source.Clear();
    }

    public bool Contains(TBase item)
    {
        return source.Contains((TDerived) item);
    }

    public void CopyTo(TBase[] array, int arrayIndex)
    {
        foreach (var item in source)
            array[arrayIndex++] = item;
    }

    public bool Remove(TBase item)
    {
        return source.Remove((TDerived) item);
    }

    public int Count
    {
        get { return source.Count; }
    }

    public bool IsReadOnly
    {
        get { return source.IsReadOnly; }
    }

    public int IndexOf(TBase item)
    {
        return source.IndexOf((TDerived) item);
    }

    public void Insert(int index, TBase item)
    {
        source.Insert(index, (TDerived) item);
    }

    public void RemoveAt(int index)
    {
        source.RemoveAt(index);
    }

    public TBase this[int index]
    {
        get { return source[index]; }
        set { source[index] = (TDerived) value; }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您可以编写如下代码:

List<Dog> dogs = new List<Dog>();
dogs.Add(new Dog { Name = "Spot", MaximumBarkDecibals = 110 });

IEnumerable<Animal> animals = dogs;
IList<Animal> animalList = new CovariantIListAdapter<Animal, Dog>(dogs);

animalList.Add(new Dog { Name = "Fluffy", MaximumBarkDecibals = 120 });
Run Code Online (Sandbox Code Playgroud)

这些更改在两个列表中都可见,因为实际上只有一个列表.适配器类只传递调用,根据需要转换项目以实现所需的IList<TBase>接口.

显然,如果你添加除Dogs之外的任何东西animalList,它会抛出异常,但这符合我的需求.


Ant*_*ram 5

从技术上讲,有数组集合。它的差异有些破损,但是可以满足您的要求。

IList<Animal> animals;
List<Dog> dogs = new List<Dog>();
animals = dogs.ToArray();
Run Code Online (Sandbox Code Playgroud)

当然,如果您尝试将a Tiger放入数组中的任何位置,那么在运行时您肯定会大吃一惊。