314

IEnumerable誰かが私に説明できますIEnumeratorか?

たとえば、いつ foreach で使用するのでしょうか? IEnumerableとはどう違いIEnumeratorますか?なぜそれを使用する必要があるのですか?

4

16 に答える 16

310

たとえば、いつ foreach で使用するのでしょうか?

IEnumerable"over"は使いませんforeach。実装IEnumerableすると使用foreach が可能になります。

次のようなコードを書く場合:

foreach (Foo bar in baz)
{
   ...
}

機能的には次のように書くのと同等です:

IEnumerator bat = baz.GetEnumerator();
while (bat.MoveNext())
{
   bar = (Foo)bat.Current
   ...
}

「機能的に同等」とは、コンパイラが実際にコードを変換するものを意味します。この例では、実装しない限りforeachonを使用できません。 baz bazIEnumerable

IEnumerablebazメソッドを実装することを意味します

IEnumerator GetEnumerator()

このIEnumeratorメソッドが返すオブジェクトは、メソッドを実装する必要があります

bool MoveNext()

Object Current()

IEnumerable最初のメソッドは、列挙子を作成したオブジェクト内の次のオブジェクトに進み、false完了した場合は戻り、2 番目のメソッドは現在のオブジェクトを返します。

.Net で反復できるものはすべて実装しIEnumerableます。独自のクラスを作成していて、それが を実装するクラスからまだ継承されていない場合は、実装することによって (および新しいメソッドが返す列挙子クラスを作成することによって)ステートメントIEnumerableでクラスを使用できるようにすることができます。foreachIEnumerableGetEnumerator

于 2009-02-17T19:41:33.280 に答える
185

IEnumerable および IEnumerator インターフェイス

既存の .NET インターフェイスを実装するプロセスを調べるために、まず IEnumerable と IEnumerator の役割を見てみましょう。C# は foreach という名前のキーワードをサポートしていることを思い出してください。これにより、任意の配列型の内容を反復処理できます。

// Iterate over an array of items.
int[] myArrayOfInts = {10, 20, 30, 40};
foreach(int i in myArrayOfInts)
{
   Console.WriteLine(i);
}

この構造を利用できるのは配列型だけに思えるかもしれませんが、実際には、GetEnumerator() という名前のメソッドをサポートするすべての型を foreach 構造で評価できます。

Garage クラスがあるとします。

// Garage contains a set of Car objects.
public class Garage
{
   private Car[] carArray = new Car[4];
   // Fill with some Car objects upon startup.
   public Garage()
   {
      carArray[0] = new Car("Rusty", 30);
      carArray[1] = new Car("Clunker", 55);
      carArray[2] = new Car("Zippy", 30);
      carArray[3] = new Car("Fred", 30);
   }
}

理想的には、データ値の配列と同じように、 foreach コンストラクトを使用して Garage オブジェクトのサブアイテムを反復処理すると便利です。

// This seems reasonable ...
public class Program
{
   static void Main(string[] args)
   {
      Console.WriteLine("***** Fun with IEnumerable / IEnumerator *****\n");
      Garage carLot = new Garage();
      // Hand over each car in the collection?
      foreach (Car c in carLot)
      {
         Console.WriteLine("{0} is going {1} MPH",
         c.PetName, c.CurrentSpeed);
      }
      Console.ReadLine();
   }
}

残念なことに、コンパイラは、Garage クラスが GetEnumerator() という名前のメソッドを実装していないことを通知します。このメソッドは、System.Collections 名前空間内に潜んでいる IEnumerable インターフェイスによって形式化されています。この動作をサポートするクラスまたは構造体は、含まれているサブアイテムを呼び出し元に公開できることをアドバタイズします (この例では、 foreach キーワード自体)。この標準 .NET インターフェイスの定義は次のとおりです。

// This interface informs the caller
// that the object's subitems can be enumerated.
public interface IEnumerable
{
   IEnumerator GetEnumerator();
}

ご覧のとおり、GetEnumerator() メソッドは、System.Collections.IEnumerator という名前のさらに別のインターフェイスへの参照を返します。このインターフェイスは、呼び出し元が IEnumerable 互換コンテナーに含まれる内部オブジェクトをトラバースできるようにするインフラストラクチャを提供します。

// This interface allows the caller to
// obtain a container's subitems.
public interface IEnumerator
{
   bool MoveNext (); // Advance the internal position of the cursor.
   object Current { get;} // Get the current item (read-only property).
   void Reset (); // Reset the cursor before the first member.
}

これらのインターフェースをサポートするように Garage タイプを更新したい場合は、長い道のりを歩んで各メソッドを手動で実装することができます。GetEnumerator()、MoveNext()、Current、および Reset() のカスタマイズされたバージョンを自由に提供できますが、もっと簡単な方法があります。System.Array 型 (および他の多くのコレクション クラス) は既に IEnumerable と IEnumerator を実装しているため、次のように要求を System.Array に委任するだけです。

using System.Collections;
...
public class Garage : IEnumerable
{
   // System.Array already implements IEnumerator!
   private Car[] carArray = new Car[4];
   public Garage()
   {
      carArray[0] = new Car("FeeFee", 200);
      carArray[1] = new Car("Clunker", 90);
      carArray[2] = new Car("Zippy", 30);
      carArray[3] = new Car("Fred", 30);
   }
   public IEnumerator GetEnumerator()
   {
      // Return the array object's IEnumerator.
      return carArray.GetEnumerator();
   }
}

Garage 型を更新したら、C# の foreach コンストラクト内でその型を安全に使用できます。さらに、GetEnumerator() メソッドがパブリックに定義されている場合、オブジェクト ユーザーは IEnumerator タイプと対話することもできます。

// Manually work with IEnumerator.
IEnumerator i = carLot.GetEnumerator();
i.MoveNext();
Car myCar = (Car)i.Current;
Console.WriteLine("{0} is going {1} MPH", myCar.PetName, myCar.CurrentSpeed);

ただし、オブジェクト レベルから IEnumerable の機能を隠したい場合は、単純に明示的なインターフェイスの実装を使用します。

IEnumerator IEnumerable.GetEnumerator()
{
  // Return the array object's IEnumerator.
  return carArray.GetEnumerator();
}

そうすることで、カジュアルなオブジェクトのユーザーは Garage の GetEnumerator() メソッドを見つけることができなくなりますが、foreach コンストラクトは必要に応じてバックグラウンドでインターフェイスを取得します。

Pro C# 5.0 および .NET 4.5 Frameworkから適応

于 2014-08-31T00:29:04.243 に答える
68

IEnumerable を実装するということは、クラスが IEnumerator オブジェクトを返すことを意味します。

public class People : IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator()
    {
        // return a PeopleEnumerator
    }
}

IEnumerator を実装するということは、クラスが反復用のメソッドとプロパティを返すことを意味します。

public class PeopleEnumerator : IEnumerator
{
    public void Reset()...

    public bool MoveNext()...

    public object Current...
}

とにかくそれが違いです。

于 2009-02-17T19:16:14.857 に答える
18

IEnumerable を実装すると、リストの IEnumerator を取得できます。

IEnumerator は、yield キーワードを使用して、リスト内の項目への foreach スタイルのシーケンシャル アクセスを許可します。

foreach 実装の前 (Java 1.4 など) では、リストを反復する方法は、リストから列挙子を取得し、次に値が返される限り、リスト内の「次の」項目を要求することでした。項目が null ではありません。Foreach は、lock() が舞台裏で Monitor クラスを実装するのと同じ方法で、言語機能として暗黙的にそれを行います。

foreach は IEnumerable を実装しているため、リストで機能することを期待しています。

于 2009-02-17T19:12:48.640 に答える
16
  • IEnumerableを実装するオブジェクトは、他のユーザーが(列挙子によって)その各項目にアクセスできるようにします。
  • IEnumeratorを実装するオブジェクトが反復を実行しています。列挙可能なオブジェクトをループしています。

列挙可能なオブジェクトをリスト、スタック、ツリーと考えてください。

于 2009-02-17T19:17:05.463 に答える
4

Iterator パターンを理解しておくと役に立ちます。同じように読むことをお勧めします。

イテレータ パターン

高レベルでは、反復子パターンを使用して、任意の型のコレクションを反復処理する標準的な方法を提供できます。イテレーター パターンには、実際のコレクション (クライアント)、アグリゲーター、イテレーターの 3 つの参加者がいます。集約は、反復子を返すメソッドを持つインターフェイス/抽象クラスです。Iterator は、コレクションを反復処理できるメソッドを持つインターフェイス/抽象クラスです。

パターンを実装するには、最初にイテレータを実装して、関連するコレクション (クライアント) を反復処理できる具象を生成する必要があります。次に、コレクション (クライアント) がアグリゲータを実装して、上記のイテレータのインスタンスを返します。

これがUMLダイアグラムです イテレータ パターン

したがって、基本的にC#では、IEnumerableは抽象集約であり、IEnumeratorは抽象Iteratorです。IEnumerable には、目的の型の IEnumerator のインスタンスを作成する役割を担う単一のメソッド GetEnumerator があります。リストなどのコレクションは、IEnumerable を実装します。

例。getPermutations(inputString)文字列のすべての順列を返すメソッドがあり、そのメソッドが次のインスタンスを返すとします。IEnumerable<string>

順列の数を数えるために、以下のようなことができます。

 int count = 0;
        var permutations = perm.getPermutations(inputString);
        foreach (string permutation in permutations)
        {
            count++;
        }

C#コンパイラは、多かれ少なかれ上記を次のように変換します

using (var permutationIterator = perm.getPermutations(input).GetEnumerator())
        {
            while (permutationIterator.MoveNext())
            {
                count++;
            }
        }

ご不明な点がございましたら、お気軽にお問い合わせください。

于 2016-10-20T15:56:56.907 に答える
2

マイナーな貢献。

それらの多くは、「いつ使用するか」と「foreach で使用する」について説明しています。IEnumerable と IEnumerator の両方の違いについて質問されたので、ここに別の州の違いを追加することを考えました。

以下のディスカッション スレッドに基づいて、以下のコード サンプルを作成しました。

IEnumerable 、IEnumerator と foreach、いつ何を使用するか IEnumerator と IEnumerable の違いは何ですか?

Enumerator は関数呼び出し間の状態 (反復位置) を保持しますが、反復では Enumerable は保持しません。

これは、理解するためのコメント付きのテスト済みの例です。

専門家は私を追加/修正してください。

static void EnumerableVsEnumeratorStateTest()
{
    IList<int> numList = new List<int>();

    numList.Add(1);
    numList.Add(2);
    numList.Add(3);
    numList.Add(4);
    numList.Add(5);
    numList.Add(6);

    Console.WriteLine("Using Enumerator - Remembers the state");
    IterateFrom1to3(numList.GetEnumerator());

    Console.WriteLine("Using Enumerable - Does not Remembers the state");
    IterateFrom1to3Eb(numList);

    Console.WriteLine("Using Enumerable - 2nd functions start from the item 1 in the collection");
}

static void IterateFrom1to3(IEnumerator<int> numColl)
{
    while (numColl.MoveNext())
    {
        Console.WriteLine(numColl.Current.ToString());

        if (numColl.Current > 3)
        {
            // This method called 3 times for 3 items (4,5,6) in the collection. 
            // It remembers the state and displays the continued values.
            IterateFrom3to6(numColl);
        }
    }
}

static void IterateFrom3to6(IEnumerator<int> numColl)
{
    while (numColl.MoveNext())
    {
        Console.WriteLine(numColl.Current.ToString());
    }
}

static void IterateFrom1to3Eb(IEnumerable<int> numColl)
{
    foreach (int num in numColl)
    {
        Console.WriteLine(num.ToString());

        if (num>= 5)
        {
            // The below method invokes for the last 2 items.
            //Since it doesnot persists the state it will displays entire collection 2 times.
            IterateFrom3to6Eb(numColl);
        }
    }
}

static void IterateFrom3to6Eb(IEnumerable<int> numColl)
{
    Console.WriteLine();
    foreach (int num in numColl)
    {
        Console.WriteLine(num.ToString());
    }
}
于 2014-04-28T18:19:10.367 に答える
2

私はこれらの違いに気づきました:

A. リストを別の方法で反復します。foreach は IEnumerable に使用でき、while ループは IEnumerator に使用できます。

B. IEnumerator は、あるメソッドから別のメソッドに渡すときに現在のインデックスを記憶できますが (現在のインデックスで動作を開始します)、IEnumerable はインデックスを記憶できず、インデックスを先頭にリセットします。このビデオの詳細https://www.youtube.com/watch?v=jd3yUjGc9M0

于 2015-07-27T08:53:10.780 に答える