0

我想制作一个列表的只读可索引“视图”,稍微扭曲只读视图应该枚举到列表类型的接口。

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。返回 an 真的不安全IEnumerator<TC>,还是编译器的推理忽略类型约束只是一个弱点?

我可以通过使用自定义枚举器来解决它,该枚举器将其包装IEnumerator<TC>并将其项目返回为T's,这当然是完全合法的,但我只是好奇为什么上述解决方案不起作用。

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; }
      }

      // ...
   }
}
4

2 回答 2

1

由于值类型,您的原始代码不起作用。您不能执行以下操作:

IEnumerable<int> ints = new[] { 1, 2 };
IEnumerable<object> os = ints;

所以编译器不会接受你的定义,因为它不能保证IEnumerable<TC>与一般兼容IEnumerable<T>。如果您添加class约束TTC编译:

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);
}
于 2013-11-06T13:36:01.140 回答
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;
于 2013-11-06T13:38:30.363 回答