web-dev-qa-db-ja.com

イテレータと列挙子の区別

.NET 3.5ジョブのインタビューの質問は、「イテレータと列挙子の違いは何ですか?」です。

これは、LINQで何をするかなど、中核となる区別です。

とにかく、違いは何ですか?ネット上でしっかりした定義を見つけることができないようです。間違えないでください。2つの用語の意味を見つけることはできますが、答えは少し異なります。インタビューのベストアンサーは何ですか?

IMO反復子はコレクションを「反復」し、列挙子は反復する機能を提供しますが、これを呼び出す必要があります。

また、yieldキーワードを使用すると、状態を保存すると言われます。この状態は正確には何ですか?この利点の例はありますか?

70
GurdeepS

C#2+では、 iterators は、コンパイラがIEnumerableおよび/またはIEnumerable <T>インターフェイスを自動的に生成する方法です。

イテレータがなければ、Current、MoveNext、Resetを含む IEnumerator を実装するクラスを作成する必要があります。これにはかなりの作業が必要です。通常、型のIEnumerator <T>を実装するプライベートクラスを作成し、yourClass.GetEnumerator()はそのプライベートクラスを構築して返します。

イテレータは、コンパイラが単純な構文(yield)を使用してこれを自動的に生成する方法です。これにより、2番目のクラス(IEnumerator)を指定せずに、GetEnumerator()をクラスに直接実装できます。そのクラスの構築は、そのすべてのメンバーとともに、あなたのために行われます。

イテレータは開発者にとって非常に使いやすいです-物事は非常に効率的な方法で、はるかに少ない労力で行われます。

Foreachを使用すると、2つは同じように動作します(カスタムIEnumeratorを正しく記述した場合)。イテレータは人生をもっとシンプルにします。

43
Reed Copsey

C#がiteratorと呼ぶものは、 generator またはgenerator function(Pythonの場合など)。ジェネレーター関数は、 coroutine の特殊なケースです。 C#イテレータ(ジェネレータ)は、enumeratorIEnumerableインターフェイスを実装するデータ型)の特別な形式です。

C#ジェネレーターのイテレーターという用語のこの使用法は、イテレーターと同じくらいの列挙子なので嫌いです。しかし、マイクロソフトが考えを変えるには遅すぎます。

対照的に、C++では、イテレータは主にコレクション内の順次要素にアクセスするために使用される値であることを考慮してください。値を取得するために拡張し、調整し、コレクションの最後に到達したかどうかをテストすることができます。

19
cdiggins

「foreachステートメントは列挙子のコンシューマーですが、反復子は列挙子のプロデューサーです。」

上記は「C#5.0 In A NutShell」がそれをどのように説明するかであり、私にとって有益でした。

つまり、foreachステートメントはMoveNext()とIEnumeratorのCurrentプロパティを使用してシーケンスを反復処理し、イテレーターはforeachステートメントで使用されるIEnumeratorの実装を生成するために使用されます。 C#では、yieldステートメントを含むイテレータメソッドを記述すると、コンパイラがプライベート列挙子を生成します。シーケンス内の項目を反復処理すると、プライベート列挙子のMoveNext()およびCurrentプロパティが呼び出されます。これらのメソッド/プロパティは、生成する値がなくなるまで値を生成するために繰り返し呼び出されるイテレータメソッドのコードによって実装されます。

これは、C#が列挙子と反復子を定義する方法についての私の理解です。

12
Ping

イテレータを理解するには、まず列挙子を理解する必要があります。

列挙子は、アイテムの順序付きリストを一度に1つずつ移動する手段を提供するスペシャリストオブジェクトです(同じ種類のものは「カーソル」と呼ばれることもあります)。 .NETフレームワークは、列挙子に関連する2つの重要なインターフェイス、IEnumeratorとIEnumerableを提供します。 IEnumeratorを実装するオブジェクトはそれ自体列挙子です。次のメンバーをサポートしています。

  • リスト上の位置を指すCurrentプロパティ

  • リストに沿って現在のアイテムを移動するMoveNextメソッド

  • resetメソッド。Currentアイテムを初期位置(最初のアイテムの前)に移動します。

一方、イテレータは列挙パターンを実装します。 .NET 2.0では、コンパイラーがエミュレートする列挙子であるイテレーターが導入されました。列挙可能なオブジェクトが直接または間接的にGetEnumertorを呼び出すと、コンパイラは生成し、適切な反復オブジェクトを返します。オプションとして、イテレータは列挙可能な列挙オブジェクトと列挙可能なオブジェクトの組み合わせにすることができます。

イテレータブロックの最も重要な要素は、歩留まりです。イテレータとエニュメレータには大きな違いが1つあります。イテレータはResetメソッドを実装しません。イテレータでResetメソッドを呼び出すと、例外が発生します。

イテレータのポイントは、列挙子を簡単に実装できるようにすることです。メソッドがアイテムの順序付きリストの列挙子または列挙可能なクラスを返す必要がある場合、「yield」ステートメントを使用して各アイテムを正しい順序で返すように記述されています。

12
Joshua

例が与えられていないので、ここに私に役立ったものがあります。

列挙子は、IEnumeratorインターフェイスを実装するクラスまたは型で.GetEnumerator()を呼び出したときに取得するオブジェクトです。このインターフェイスが実装されると、foreachを使用してコレクションを「反復」できるようにするために、コンパイラに必要なすべてのコードが作成されました。

ただし、イテレータと混同しないでください。列挙子とイテレータの両方で「反復」できます。列挙と反復は基本的に同じプロセスですが、実装方法が異なります。列挙は、IEnumeratorインターフェイスを実装したことを意味します。反復とは、クラスでイテレータ構造を作成し(以下に示す)、クラスでforeachを呼び出していることを意味します。この時点で、コンパイラは自動的に列挙子機能を作成します。

また、列挙子でしゃがむ必要はありません。 MyClass.GetEnumerator()を終日呼び出すことができますが、何もしません(例:

IEnumerator myEnumeratorThatIWillDoNothingWith = MyClass.GetEnumerator())。

また、クラスのイテレータ構造は、実際に使用しているときにのみ実際に使用されることに注意してください。つまり、クラスでforeachを呼び出しました。

msdn の反復子の例を次に示します。

public class DaysOfTheWeek : System.Collections.IEnumerable
{

     string[] days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };

     //This is the iterator!!!
     public System.Collections.IEnumerator GetEnumerator()
     {
         for (int i = 0; i < days.Length; i++)
         {
             yield return days[i];
         }
     }

}

class TestDaysOfTheWeek
{
    static void Main()
    {
        // Create an instance of the collection class
        DaysOfTheWeek week = new DaysOfTheWeek();

        // Iterate with foreach - this is using the iterator!!! When the compiler
        //detects your iterator, it will automatically generate the Current, 
        //MoveNext and Dispose methods of the IEnumerator or IEnumerator<T> interface
        foreach (string day in week)
        {
            System.Console.Write(day + " ");
        }
    }
}
// Output: Sun Mon Tue Wed Thr Fri Sat
7
richard

「イテレータはC#2.0の新機能です。イテレータは、IEnumerableインターフェイス全体を実装しなくても、クラスまたは構造体でforeach反復をサポートできるメソッド、取得アクセサまたは演算子です。代わりに、イテレータのみを提供します。クラス内のデータ構造を単純にたどります。コンパイラがイテレータを検出すると、IEnumerableまたはIEnumerableインターフェイスのCurrent、MoveNext、およびDisposeメソッドを自動的に生成します。 - msdn

3
John Ellinwood

列挙はオブジェクトを扱い、反復は値のみを扱います。ベクトルハッシュテーブルなどを使用するときに列挙が使用され、whileループforループなどで反復が使用されます。yieldキーワードを使用したことがないため、説明できません。

2
Kredns