反復子 (C#)Iterators (C#)

反復子を使用して、リストや配列などのコレクションをステップ実行することができます。An iterator can be used to step through collections such as lists and arrays.

iterator メソッドまたは get アクセサーは、コレクションに対するカスタム イテレーションを実行します。An iterator method or get accessor performs a custom iteration over a collection. 反復子メソッドは、yield return ステートメントを使用して、各要素を 1 回に 1 つ返します。An iterator method uses the yield return statement to return each element one at a time. yield return ステートメントに達すると、コードの現在の場所が記憶されます。When a yield return statement is reached, the current location in code is remembered. 次回、iterator 関数が呼び出されると、この位置から実行が再開されます。Execution is restarted from that location the next time the iterator function is called.

foreach ステートメントまたは LINQ クエリを使用して、クライアント コードから反復子を呼び出します。You consume an iterator from client code by using a foreach statement or by using a LINQ query.

次の例では、foreach ループの最初の反復子により、最初の yield return ステートメントに達するまで SomeNumbers iterator メソッドで実行が続行されます。In the following example, the first iteration of the foreach loop causes execution to proceed in the SomeNumbers iterator method until the first yield return statement is reached. このイテレーションは 3 の値を返し、iterator メソッドの現在の場所が保持されます。This iteration returns a value of 3, and the current location in the iterator method is retained. ループの次のイテレーションでは、iterator メソッドの実行が中断した場所から続行し、yield return ステートメントに達したときに再度停止します。On the next iteration of the loop, execution in the iterator method continues from where it left off, again stopping when it reaches a yield return statement. このイテレーションは 5 の値を返し、ここでも iterator メソッドの現在の場所が保持されます。This iteration returns a value of 5, and the current location in the iterator method is again retained. iterator メソッドの最後に達すると、ループが完了します。The loop completes when the end of the iterator method is reached.

static void Main()  
{  
    foreach (int number in SomeNumbers())  
    {  
        Console.Write(number.ToString() + " ");  
    }  
    // Output: 3 5 8  
    Console.ReadKey();  
}  

public static System.Collections.IEnumerable SomeNumbers()  
{  
    yield return 3;  
    yield return 5;  
    yield return 8;  
}  

Iterator メソッドまたは get アクセサーの戻り値の型は、IEnumerableIEnumerable<T>IEnumerator、または IEnumerator<T> となります。The return type of an iterator method or get accessor can be IEnumerable, IEnumerable<T>, IEnumerator, or IEnumerator<T>.

yield break ステートメントを使用すると、反復を終了できます。You can use a yield break statement to end the iteration.

反復子は、Visual Studio 2005 の C# で導入されました。Iterators were introduced in C# in Visual Studio 2005.

このトピックの内容In this topic

注意

このトピックの単純な反復子の例を除くすべての例には、System.Collections および System.Collections.Generic 名前空間の using ディレクティブが含まれています。For all examples in this topic except the Simple Iterator example, include using directives for the System.Collections and System.Collections.Generic namespaces.

単純な反復子Simple Iterator

次の例では、for ループ内に 1 つの yield return ステートメントが含まれます。The following example has a single yield return statement that is inside a for loop. Main では、foreach ステートメント本文の各イテレーションで iterator 関数が呼び出され、これが次の yield return ステートメントに続行されます。In Main, each iteration of the foreach statement body creates a call to the iterator function, which proceeds to the next yield return statement.

static void Main()  
{  
    foreach (int number in EvenSequence(5, 18))  
    {  
        Console.Write(number.ToString() + " ");  
    }  
    // Output: 6 8 10 12 14 16 18  
    Console.ReadKey();  
}  

public static System.Collections.Generic.IEnumerable<int>  
    EvenSequence(int firstNumber, int lastNumber)  
{  
    // Yield even numbers in the range.  
    for (int number = firstNumber; number <= lastNumber; number++)  
    {  
        if (number % 2 == 0)  
        {  
            yield return number;  
        }  
    }  
}  

コレクション クラスを作成するCreating a Collection Class

次の例の DaysOfTheWeek クラスは、GetEnumerator メソッドを必要とする IEnumerable インターフェイスを実装します。In the following example, the DaysOfTheWeek class implements the IEnumerable interface, which requires a GetEnumerator method. コンパイラは、IEnumerator を返す GetEnumerator メソッドを暗黙的に呼び出します。The compiler implicitly calls the GetEnumerator method, which returns an IEnumerator.

GetEnumerator メソッドは、yield return ステートメントを使用して、各文字列を一度に 1 つ返します。The GetEnumerator method returns each string one at a time by using the yield return statement.

static void Main()  
{  
    DaysOfTheWeek days = new DaysOfTheWeek();  

    foreach (string day in days)  
    {  
        Console.Write(day + " ");  
    }  
    // Output: Sun Mon Tue Wed Thu Fri Sat  
    Console.ReadKey();  
}  

public class DaysOfTheWeek : IEnumerable  
{  
    private string[] days = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };  

    public IEnumerator GetEnumerator()  
    {  
        for (int index = 0; index < days.Length; index++)  
        {  
            // Yield each day of the week.  
            yield return days[index];  
        }  
    }  
}  

次の例では、動物のコレクションを含む Zoo クラスを作成します。The following example creates a Zoo class that contains a collection of animals.

クラス インスタンス (theZoo) を参照する foreach ステートメントでは、GetEnumerator メソッドが暗黙的に呼び出されます。The foreach statement that refers to the class instance (theZoo) implicitly calls the GetEnumerator method. Birds および Mammals プロパティを参照する foreach ステートメントでは、AnimalsForType という名前の iterator メソッドが使用されます。The foreach statements that refer to the Birds and Mammals properties use the AnimalsForType named iterator method.

static void Main()  
{  
    Zoo theZoo = new Zoo();  

    theZoo.AddMammal("Whale");  
    theZoo.AddMammal("Rhinoceros");  
    theZoo.AddBird("Penguin");  
    theZoo.AddBird("Warbler");  

    foreach (string name in theZoo)  
    {  
        Console.Write(name + " ");  
    }  
    Console.WriteLine();  
    // Output: Whale Rhinoceros Penguin Warbler  

    foreach (string name in theZoo.Birds)  
    {  
        Console.Write(name + " ");  
    }  
    Console.WriteLine();  
    // Output: Penguin Warbler  

    foreach (string name in theZoo.Mammals)  
    {  
        Console.Write(name + " ");  
    }  
    Console.WriteLine();  
    // Output: Whale Rhinoceros  

    Console.ReadKey();  
}  

public class Zoo : IEnumerable  
{  
    // Private members.  
    private List<Animal> animals = new List<Animal>();  

    // Public methods.  
    public void AddMammal(string name)  
    {  
        animals.Add(new Animal { Name = name, Type = Animal.TypeEnum.Mammal });  
    }  

    public void AddBird(string name)  
    {  
        animals.Add(new Animal { Name = name, Type = Animal.TypeEnum.Bird });  
    }  

    public IEnumerator GetEnumerator()  
    {  
        foreach (Animal theAnimal in animals)  
        {  
            yield return theAnimal.Name;  
        }  
    }  

    // Public members.  
    public IEnumerable Mammals  
    {  
        get { return AnimalsForType(Animal.TypeEnum.Mammal); }  
    }  

    public IEnumerable Birds  
    {  
        get { return AnimalsForType(Animal.TypeEnum.Bird); }  
    }  

    // Private methods.  
    private IEnumerable AnimalsForType(Animal.TypeEnum type)  
    {  
        foreach (Animal theAnimal in animals)  
        {  
            if (theAnimal.Type == type)  
            {  
                yield return theAnimal.Name;  
            }  
        }  
    }  

    // Private class.  
    private class Animal  
    {  
        public enum TypeEnum { Bird, Mammal }  

        public string Name { get; set; }  
        public TypeEnum Type { get; set; }  
    }  
}  

ジェネリック リストと共に反復子を使用するUsing Iterators with a Generic List

次の例の Stack(Of T) ジェネリック クラスは、IEnumerable<T> ジェネリック インターフェイスを実装しています。In the following example, the Stack(Of T) generic class implements the IEnumerable<T> generic interface. Push メソッドでは、T 型の配列に値を割り当てます。The Push method assigns values to an array of type T. GetEnumerator メソッドは、yield return ステートメントを使って配列値を返します。The GetEnumerator method returns the array values by using the yield return statement.

ジェネリック メソッド GetEnumerator だけでなく、非ジェネリック メソッド GetEnumerator も実装する必要があります。In addition to the generic GetEnumerator method, the non-generic GetEnumerator method must also be implemented. これは、IEnumerable<T>IEnumerable から継承するためです。This is because IEnumerable<T> inherits from IEnumerable. 非ジェネリック実装は、ジェネリック実装に従います。The non-generic implementation defers to the generic implementation.

例では名前付き反復子を使用して、同じデータ コレクションでのさまざまな反復処理をサポートします。The example uses named iterators to support various ways of iterating through the same collection of data. この場合の名前付き反復子は、TopToBottom プロパティと BottomToTop プロパティ、および TopN メソッドです。These named iterators are the TopToBottom and BottomToTop properties, and the TopN method.

BottomToTop プロパティは get アクセサーで反復子を使用します。The BottomToTop property uses an iterator in a get accessor.

static void Main()  
{  
    Stack<int> theStack = new Stack<int>();  

    //  Add items to the stack.  
    for (int number = 0; number <= 9; number++)  
    {  
        theStack.Push(number);  
    }  

    // Retrieve items from the stack.  
    // foreach is allowed because theStack implements  
    // IEnumerable<int>.  
    foreach (int number in theStack)  
    {  
        Console.Write("{0} ", number);  
    }  
    Console.WriteLine();  
    // Output: 9 8 7 6 5 4 3 2 1 0  

    // foreach is allowed, because theStack.TopToBottom  
    // returns IEnumerable(Of Integer).  
    foreach (int number in theStack.TopToBottom)  
    {  
        Console.Write("{0} ", number);  
    }  
    Console.WriteLine();  
    // Output: 9 8 7 6 5 4 3 2 1 0  

    foreach (int number in theStack.BottomToTop)  
    {  
        Console.Write("{0} ", number);  
    }  
    Console.WriteLine();  
    // Output: 0 1 2 3 4 5 6 7 8 9  

    foreach (int number in theStack.TopN(7))  
    {  
        Console.Write("{0} ", number);  
    }  
    Console.WriteLine();  
    // Output: 9 8 7 6 5 4 3  

    Console.ReadKey();  
}  

public class Stack<T> : IEnumerable<T>  
{  
    private T[] values = new T[100];  
    private int top = 0;  

    public void Push(T t)  
    {  
        values[top] = t;  
        top++;  
    }  
    public T Pop()  
    {  
        top--;  
        return values[top];  
    }  

    // This method implements the GetEnumerator method. It allows  
    // an instance of the class to be used in a foreach statement.  
    public IEnumerator<T> GetEnumerator()  
    {  
        for (int index = top - 1; index >= 0; index--)  
        {  
            yield return values[index];  
        }  
    }  

    IEnumerator IEnumerable.GetEnumerator()  
    {  
        return GetEnumerator();  
    }  

    public IEnumerable<T> TopToBottom  
    {  
        get { return this; }  
    }  

    public IEnumerable<T> BottomToTop  
    {  
        get  
        {  
            for (int index = 0; index <= top - 1; index++)  
            {  
                yield return values[index];  
            }  
        }  
    }  

    public IEnumerable<T> TopN(int itemsFromTop)  
    {  
        // Return less than itemsFromTop if necessary.  
        int startIndex = itemsFromTop >= top ? 0 : top - itemsFromTop;  

        for (int index = top - 1; index >= startIndex; index--)  
        {  
            yield return values[index];  
        }  
    }  

}  

構文情報Syntax Information

反復子は、メソッドまたは get アクセサーとして指定できます。An iterator can occur as a method or get accessor. 反復子を、イベント、インスタンス コンストラクター、静的コンストラクター、静的ファイナライザーで指定することはできません。An iterator cannot occur in an event, instance constructor, static constructor, or static finalizer.

yield return ステートメント内の式の型から反復子の戻り値の型への暗黙的な変換が存在する必要があります。An implicit conversion must exist from the expression type in the yield return statement to the return type of the iterator.

C# の場合、iterator メソッドで ref パラメーターや out パラメーターを指定することはできません。In C#, an iterator method cannot have any ref or out parameters.

C# の場合、"yield" は予約語ではなく、return または break キーワードの前に使用される場合にのみ、特別な意味を持ちます。In C#, "yield" is not a reserved word and has special meaning only when it is used before a return or break keyword.

技術的な実装Technical Implementation

メソッドとして反復子を記述しても、コンパイラが入れ子のクラス (つまり、事実上、ステート マシン) に変換します。Although you write an iterator as a method, the compiler translates it into a nested class that is, in effect, a state machine. このクラスは、クライアント コードで foreach ループが続く限り、反復子の位置を追跡します。This class keeps track of the position of the iterator as long the foreach loop in the client code continues.

コンパイラの動作を確認するには、Ildasm.exe ツールを使用して、iterator メソッドに対して生成される Microsoft 中間言語コードを表示します。To see what the compiler does, you can use the Ildasm.exe tool to view the Microsoft intermediate language code that is generated for an iterator method.

クラスまたは構造体用の反復子を作成する場合、IEnumerator インターフェイス全体を実装する必要はありません。When you create an iterator for a class or struct, you don't have to implement the whole IEnumerator interface. コンパイラは、反復子を検出すると、IEnumerator または IEnumerator<T> インターフェイスの CurrentMoveNext、および Dispose メソッドを自動的に生成します。When the compiler detects the iterator, it automatically generates the Current, MoveNext, and Dispose methods of the IEnumerator or IEnumerator<T> interface.

foreach ループの連続する反復ごとに (または IEnumerator.MoveNext を直接呼び出すと)、前の yield return ステートメントの後で次の反復子コード本体が再開されます。On each successive iteration of the foreach loop (or the direct call to IEnumerator.MoveNext), the next iterator code body resumes after the previous yield return statement. その後、反復子本体の最後に到達するか、yield break ステートメントが検出されるまで、次の yield return ステートメントに続行されます。It then continues to the next yield return statement until the end of the iterator body is reached, or until a yield break statement is encountered.

反復子は、IEnumerator.Reset メソッドをサポートしません。Iterators don't support the IEnumerator.Reset method. 反復処理を最初から再度行う場合は、新しい反復子を取得する必要があります。To re-iterate from the start, you must obtain a new iterator.

詳細については、「C# 言語の仕様」を参照してください。For additional information, see the C# Language Specification.

反復子の使用Use of Iterators

反復子を使用すると、複雑なコードを使用して一覧シーケンスを設定する必要がある場合に、foreach ループの単純さを維持することができます。Iterators enable you to maintain the simplicity of a foreach loop when you need to use complex code to populate a list sequence. これは次のような場合に役立ちます。This can be useful when you want to do the following:

  • 最初の foreach ループ イテレーションの後に一覧シーケンスを変更する。Modify the list sequence after the first foreach loop iteration.

  • 最初の foreach ループ イテレーションの前に大きい一覧が完全に読み込まれないようにする。Avoid fully loading a large list before the first iteration of a foreach loop. 例として、ページ フェッチでのテーブル行のバッチの読み込みなどがあります。An example is a paged fetch to load a batch of table rows. また、別の例として、EnumerateFiles メソッドでの .NET Framework 内の反復子の実装があります。Another example is the EnumerateFiles method, which implements iterators within the .NET Framework.

  • 反復子に一覧の作成をカプセル化する。Encapsulate building the list in the iterator. iterator メソッドでは、一覧を作成してから、ループで各結果を生成することができます。In the iterator method, you can build the list and then yield each result in a loop.

関連項目See Also

System.Collections.Generic
IEnumerable<T>
foreach、inforeach, in
yieldyield
配列での foreach の使用Using foreach with Arrays
ジェネリックGenerics