80

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

これは、LINQ などで行うべき重要な違いです。

とにかく、違いは何ですか?ネット上で明確な定義を見つけることができないようです。間違いなく、この 2 つの用語の意味は理解できますが、得られる答えはわずかに異なります。面接のベストアンサーは?

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

また、yield キーワードを使用すると、状態が保存されると言われています。この状態とは一体何なのでしょうか?この利益が発生した例はありますか?

4

9 に答える 9

61

反復とは、いくつかのステップを繰り返すことを意味し、列挙とは、値のコレクション内のすべての値を調べることを意味します。したがって、列挙には通常、何らかの形の反復が必要です。

このように、列挙は、ステップがコレクションから値を取得する反復の特殊なケースです。

「通常」に注意してください。列挙は再帰的に実行することもできますが、再帰と反復は非常に密接に関連しているため、この小さな違いは気にしません。

コレクションに明示的に保存しない値を列挙することもできます。たとえば、自然数、素数などを列挙できますが、列挙中にこれらの値を計算し、物理的なコレクションから取得することはありません。このケースは、何らかのロジックによって値が定義された仮想コレクションを列挙するものとして理解できます。


リード・コプシーは要点をつかんだと思います。C# では、何かを列挙する主な方法が 2 つあります。

  1. 実装Enumerableと実装するクラスIEnumerator
  2. yieldステートメントでイテレータを実装する

最初の方法は実装が難しく、列挙にオブジェクトを使用します。2 番目の方法は実装が簡単で、継続を使用します。

于 2009-04-04T01:16:45.157 に答える
47

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

反復子がない場合は、Current、MoveNext、および Reset を含むIEnumeratorを実装するクラスを作成する必要があります。これにはかなりの作業が必要です。通常、タイプに IEnumerator<T> を実装するプライベート クラスを作成すると、 yourClass.GetEnumerator() がそのプライベート クラスを構築して返します。

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

イテレータは非常に開発者にとって使いやすいものです。非常に効率的な方法で作業が行われ、労力ははるかに少なくなります。

foreach を使用すると、2 つの動作は同じになります (カスタム IEnumerator を正しく記述している場合)。イテレータは生活をよりシンプルにします。

于 2009-04-04T01:18:22.797 に答える
22

C# がイテレータと呼ぶものは、より一般的に (C# の世界の外では)ジェネレーターまたはジェネレーター関数(Python など) と呼ばれます。ジェネレーター関数は、コルーチンの特殊なケースです。AC# イテレーター (ジェネレーター) は、特殊な形式の列挙子IEnumerable(インターフェイスを実装するデータ型) です。

C# ジェネレーターに対するイテレーターという用語のこの使用法は嫌いです。イテレーターであると同時に列挙子でもあるからです。ただし、Microsoft が考えを変えるには遅すぎます。

対照的に、C++ では、反復子は主にコレクション内の順次要素にアクセスするために使用される値であると考えてください。これを進めて、値を取得するために推論し、コレクションの最後に達したかどうかを確認するためにテストすることができます。

于 2009-10-27T11:50:48.387 に答える
16

「 foreach ステートメントは列挙子の消費者ですが、反復子は列挙子の生産者です。」

上記は「C# 5.0 In A NutShell」で説明されており、私にとって役に立ちました。

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

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

于 2014-07-23T01:28:12.960 に答える
12

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

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

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

  • 現在の項目をリストに沿って 1 つ移動する MoveNext メソッド

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

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

イテレータ ブロックの必須要素は、yield ステートメントです。イテレータと列挙子には大きな違いが 1 つあります。イテレータは Reset メソッドを実装していません。イテレータで Reset メソッドを呼び出すと、例外が発生します。

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

于 2009-04-04T01:25:23.687 に答える
9

例が示されていなかったので、これが私にとって役に立ちました。

列挙子は、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
于 2011-02-02T18:28:42.847 に答える
4

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

于 2009-04-04T01:12:30.003 に答える
2

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

于 2009-04-04T01:11:34.437 に答える