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 で制約しました。 を返すのは本当に安全ではないでしょうか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; }
      }

      // ...
   }
}
4

2 に答える 2

1

値の型が原因で、元のコードが機能しません。次のことはできません。

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

そのため、一般的にIEnumerable<TC>互換性があることを保証できないため、コンパイラは定義を受け入れません。制約をIEnumerable<T>追加してコンパイルすると、次のようになります。classTTC

internal class ListIndexable<TC, T> : IIndexable<T> where TC : class, T where T : class
{
}

ListIndexableただし、型パラメーターを 2 つではなく 1 つだけ定義できます。定義を次のように変更した場合:

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();

whereAsIndexableは次のように簡単に定義できます。

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 に答える