我想创建一个列表的只读索引“视图”,只读一下这个视图应枚举到列表类型的接口。
interface IIndexable<out T> : IEnumerable<T>
{
T this[int i] { get; }
int Count { get; }
}
使用场景看起来像这样。
List<Dog> dogs = new List<Dog>[]{ dog1, dog2 }; // Dog implements IAnimal
IIndexable<IAnimal> animals = dogs.AsIIndexable<Dog, IAnimal>();
IAnimal first = animals[0];
我认为考虑到协方差和类型限制,这是可能的,但也许我在这里弄错了。 我的第一次尝试看起来像这样,由于枚举器的类型而无效。
internal class ListIndexable<TC, T> : IIndexable<T> where TC : T
{
private List<TC> list;
public ListIndexable(List<TC> list) { this.list = list; }
public T this[int i] { get { return list[i]; } }
public IEnumerator<T> GetEnumerator()
{
return list.GetEnumerator(); // Computer says no.
}
}
为什么我不能将IEnumerator<TC>
作为IEnumerator<T>
返回?这种类型是协变的,我用TC约束它:T。返回IEnumerator<TC>
真的不安全,或者这只是编译器推理忽略类型约束的弱点吗?
我可以使用自定义枚举器来解决它,它包装IEnumerator<TC>
并将其项目作为T
返回,这当然是完全合法的,但我只是好奇为什么上述解决方案可以不行。
internal class ListIndexable<TC, T> : IIndexable<T> where TC : T
{
private List<TC> list;
public ListIndexable(List<TC> list) { this.list = list; }
public T this[int i] { get { return list[i]; } }
public IEnumerator<T> GetEnumerator()
{
return new Enumerator(list.GetEnumerator());
}
class Enumerator : IEnumerator<T>
{
private IEnumerator<TC> inner;
internal Enumerator(IEnumerator<TC> inner) { this.inner = inner; }
public bool MoveNext()
{
return inner.MoveNext();
}
public T Current
{
get { return inner.Current; }
}
// ...
}
}
答案 0 :(得分:1)
由于值类型原因,您的原始代码无效。您无法执行以下操作:
IEnumerable<int> ints = new[] { 1, 2 };
IEnumerable<object> os = ints;
所以编译器不接受你的定义,因为它不能保证IEnumerable<TC>
一般与IEnumerable<T>
兼容。如果您将class
约束添加到T
和TC
,则会编译:
internal class ListIndexable<TC, T> : IIndexable<T> where TC : class, T where T : class
{
}
但是,您只能使用一个类型参数而不是两个来定义ListIndexable
。如果您将定义更改为:
internal class ListIndexable<T> : IIndexable<T>
{
private List<T> list;
public ListIndexable(List<T> list) { this.list = list; }
public T this[int i] { get { return list[i]; } }
public IEnumerator<T> GetEnumerator()
{
return list.GetEnumerator();
}
}
然后你可以这样做:
IIndexable<IAnimal> animals = dogs.AsIIndexable();
其中AsIndexable
可以简单地定义为:
public static ListIndexable<T> AsIndexable<T>(this List<T> list)
{
return new ListIndexable<T>(list);
}
答案 1 :(得分:1)
如果你只需要创建一个只读集合,你可以使用ReadOnlyCollection来包装你的列表,例如:
var dogs = new List<Dog> {new Dog{Name="A"}, new Dog{Name="B"}, new Dog{Name="C"}};
var ro=new ReadOnlyCollection<Dog>(dogs);
IEnumerable<IAnimal> a = ro;