web-dev-qa-db-ja.com

IEnumerable、IEnumeratorとforeach、いつ何を使用するか

IEnumerableとIEnumeratorを使用していましたが、1つのポイントを明確に取得できませんでした。foreachがある場合、なぜこの2つのインターフェイスが必要なのですか?インターフェイスを使用する必要があるシナリオはありますか?はいの場合、誰かが例を挙げて説明できますか?任意の提案や意見を歓迎します。ありがとう。

23
Wondering

foreachses多くの場合インターフェース。 implementforeachが使用できるシーケンスを実行する場合はインターフェイスが必要です。 (ただし、イテレータブロックは通常、この実装タスクを非常に単純にします。)

ただし、ときどきイテレータを直接使用すると便利な場合があります。良い例は、2つの異なるシーケンスを「ペアリング」しようとする場合です。たとえば、名前と年齢の2つのシーケンスを受け取り、2つを一緒に印刷するとします。あなたは書くかもしれません:

static void PrintNamesAndAges(IEnumerable<string> names, IEnumerable<int> ages)
{
    using (IEnumerator<int> ageIterator = ages.GetEnumerator())
    {
        foreach (string name in names)
        {
            if (!ageIterator.MoveNext())
            {
                throw new ArgumentException("Not enough ages");
            }
            Console.WriteLine("{0} is {1} years old", name, ageIterator.Current);
        }
        if (ageIterator.MoveNext())
        {
            throw new ArgumentException("Not enough names");
        }

    }
}

同様に、最初の項目を他の項目とは異なる方法で処理する(たとえば)場合は、イテレータを使用すると便利です。

public T Max<T>(IEnumerable<T> items)
{
    Comparer<T> comparer = Comparer<T>.Default;

    using (IEnumerator<T> iterator = items.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            throw new InvalidOperationException("No elements");
        }
        T currentMax = iterator.Current;

        // Now we've got an initial value, loop over the rest
        while (iterator.MoveNext())
        {
            T candidate = iterator.Current;
            if (comparer.Compare(candidate, currentMax) > 0)
            {
                currentMax = candidate;
            }
        }
        return currentMax;
    }
}

ここで、IEnumerator<T>IEnumerable<T>の違いに興味がある場合は、データベースの用語で考えてみてください。IEnumerable<T>をテーブル、IEnumerator<T>カーソルとして。テーブルに新しいカーソルを与えるように依頼することができ、同じテーブル上に同時に複数のカーソルを置くことができます。

この違いを実際に理解するには時間がかかる場合がありますが、リスト(または配列など)には「リストのどこにいるか」という概念はなく、そのリスト/配列/その他のイテレータがあることを覚えておいてください。 doesその状態のビットが役立ちます。

50
Jon Skeet

ジョンが言ったこと。

  • IEnumerableまたはIEnumerable<T>:これを実装することにより、オブジェクトは、シーケンス/コレクション/セットをトラバースするために使用できるイテレータを提供できることを示します
  • IEnumeratorまたはIEnumerator<T>:前のインターフェースで定義されたGetEnumeratorメソッドを呼び出すと、IEnumerator参照としてイテレーターオブジェクトを取得します。これにより、MoveNext()を呼び出して、Currentオブジェクトを取得できます。
  • foreach:内部でどのように機能するかを知る必要がないという意味で、C#コンストラクト/ファサードです。内部的にイテレータを取得し、適切なメソッドを呼び出して、各アイテム(foreachブロックのコンテンツ)で何をしたいかに集中します。ほとんどの場合、独自のタイプまたはカスタム反復を実装している場合を除き、foreachが必要です。実装している場合は、最初の2つのインターフェイスについて理解する必要があります。
8
Gishu

あなたはしないことを覚えておいてください 持ってる foreach --IIRCを使用するIEnumeratorとそのバリアントを実装するために必要なのは、boolを返すMoveNext()メソッドを持つオブジェクトを返すGetEnumerator()メソッドと、オブジェクトを返すCurrentプロパティだけです。 IEnumeratorとIEnumerableを使用する必要はありませんが、通常は使用することをお勧めします。詳細については、 ここ を参照してください。

6
thecoop