Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 协方差与IList_C#_.net_.net 4.0_Covariance - Fatal编程技术网

C# 协方差与IList

C# 协方差与IList,c#,.net,.net-4.0,covariance,C#,.net,.net 4.0,Covariance,我想要一个协变集合,它的项可以通过索引检索。IEnumerable是我所知道的唯一一个协变的.net集合,但它没有这种索引支持 具体来说,我想这样做: List<Dog> dogs = new List<Dog>(); IEnumerable<Animal> animals = dogs; IList<Animal> animalList = dogs; // This line does not compile List dogs=new L

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

具体来说,我想这样做:

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

IEnumerable<Animal> animals = dogs;
IList<Animal> animalList = dogs; // This line does not compile
List dogs=new List();
i可数动物=狗;
IList animalList=狗;//这一行不编译
现在,我知道为什么这是一个问题。列表实现具有Add方法的ICollection。通过向上转换到动物的
IList
,它将允许后续代码添加“real”
列表中不允许的任何类型的动物


那么,有人知道支持协变索引查找的集合吗?我不想创建我自己的。

更新:从.NET4.5开始,有和都是协变的;后者基本上是
IEnumerable
Count
;前者添加了
T这个[int索引]{get;}
。还应注意,
IEnumerable
从.NET 4.0开始是协变的

List
和(via)都实现了这两个功能


只有当它只有一个
get
索引器,即

public T this[int index] { get; }
但是所有的主要集合都有
{get;set;}
,这让它很尴尬。我不知道有哪种方法可以满足要求,但您可以将其包装起来,即编写一个扩展方法:

var covariant = list.AsCovariant();
这是一个围绕
IList
的包装器,它只公开
IEnumerable
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; }
}
公共静态类协方差
{
公共静态IIndexedEnumerable变量(此IList尾部)
{
返回新的协方差列表(尾部);
}
私有类协方差列表:IIndexedEnumerable
{
私有只读IList尾部;
公共协方差列表(IList尾部)
{
this.tail=tail;
}
public T this[int index]{get{return tail[index];}
公共IEnumerator GetEnumerator(){return tail.GetEnumerator();}
IEnumerator IEnumerable.GetEnumerator(){return tail.GetEnumerator();}
公共整数计数{get{return tail.Count;}}
}
}
公共接口IIndexedEnumerable:IEnumerable
{
T此[int索引]{get;}
整数计数{get;}
}

从技术上讲,这里有数组集合。它的方差有点小,但它会按你的要求去做

IList<Animal> animals;
List<Dog> dogs = new List<Dog>();
animals = dogs.ToArray();
IList动物;
列表狗=新列表();
动物=狗;

当然,如果您尝试将
老虎
放在数组中的任何位置,在运行时都会非常惊人地爆炸。

我编写了一个类来解决这个问题:

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; }
    }
}
公共类协方差适配器:IList
来源:TBase
{
私人来源;
公共统计数据适配器(IList源)
{
this.source=源;
}
公共IEnumerator GetEnumerator()
{
foreach(源中的var项)
收益回报项目;
}
IEnumerator IEnumerable.GetEnumerator()
{
返回GetEnumerator();
}
公共作废添加(TBase项)
{
来源.添加((t派生)项);
}
公共空间清除()
{
source.Clear();
}
公共布尔包含(TBase项)
{
返回来源。包含((TDerived)项);
}
public void CopyTo(TBase[]数组,int arrayIndex)
{
foreach(源中的var项)
数组[arrayIndex++]=项;
}
公共布尔删除(TBase项)
{
返回来源。删除((t派生)项);
}
公共整数计数
{
获取{return source.Count;}
}
公共图书馆是只读的
{
获取{return source.IsReadOnly;}
}
公共内部索引(TBase项目)
{
返回source.IndexOf((t派生)项);
}
公共作废插入(内部索引,TBase项)
{
来源.插入(索引,(t派生)项);
}
公共无效删除(整数索引)
{
来源:RemoveAt(索引);
}
公共t基于此[int索引]
{
获取{返回源[索引];}
设置{source[index]=(TDerived)值;}
}
}
现在您可以编写如下代码:

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 });
List dogs=new List();
添加(新的Dog{Name=“Spot”,maximumbardecibals=110});
i可数动物=狗;
IList animalList=新的协变适应者(狗);
添加(新狗{Name=“Fluffy”,maximumbardecibals=120});
更改在两个列表中都可见,因为实际上只有一个列表。adapter类只是传递调用,根据需要转换项以实现所需的
IList
接口


显然,如果您向
animalList
添加除狗以外的任何内容,它将引发异常,但这满足了我的需要。

从.NET Framework 4.5开始,存在一个协变的接口IReadOnlyList。它本质上与Mark Gravell回答中的IIndexedEnumerable界面相同

IReadOnlyList的实现方式如下:

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 });
//
///表示可由索引访问的元素的只读集合。
/// 
///只读列表中元素的类型。此类型参数是协变的。也就是说,您可以使用您指定的类型,也可以使用更派生的任何类型。有关协方差和逆变换的更多信息,请参见泛型中的协方差和逆变换。
公共接口IReadOnlyList:IReadOnlyCollection、IEnumerable、IEnumerable
{
/// 
///获取只读列表中指定索引处的元素。
/// 
/// 
/// 
///只读列表中指定索引处的元素。
/// 
///要获取的元素的从零开始的索引。
T此[int索引]{get;}
}

+1…在.NET中缺少只读集合接口(除了
IEnumerable
)使得这几乎不可能,但我认为这是一个常见的问题