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)
这是我为解决这个问题而编写的一个类:
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,它会抛出异常,但这符合我的需求.
从技术上讲,有数组集合。它的差异有些破损,但是可以满足您的要求。
IList<Animal> animals;
List<Dog> dogs = new List<Dog>();
animals = dogs.ToArray();
Run Code Online (Sandbox Code Playgroud)
当然,如果您尝试将a Tiger放入数组中的任何位置,那么在运行时您肯定会大吃一惊。
| 归档时间: |
|
| 查看次数: |
7466 次 |
| 最近记录: |