Iteratoren (C#)Iterators (C#)

Ein Iterator kann verwendet werden, um Auflistungen wie Listen und Arrays schrittweise durchzugehen.An iterator can be used to step through collections such as lists and arrays.

Eine Iteratormethode oder eine get-Zugriffsmethode führt eine benutzerdefinierte Iteration einer Auflistung durch.An iterator method or get accessor performs a custom iteration over a collection. Eine Iteratormethode verwendet die yield return-Anweisung, um jedes Element einzeln nacheinander zurückzugeben.An iterator method uses the yield return statement to return each element one at a time. Wenn eine yield return-Anweisung erreicht wird, wird die aktuelle Position im Code gespeichert.When a yield return statement is reached, the current location in code is remembered. Wenn die Iteratorfunktion das nächste Mal aufgerufen wird, wird die Ausführung von dieser Position neu gestartet.Execution is restarted from that location the next time the iterator function is called.

Sie erzeugen einen Iterator aus einem Clientcode, indem Sie eine foreach-Anweisung oder eine LINQ-Abfrage verwenden.You consume an iterator from client code by using a foreach statement or by using a LINQ query.

In folgendem Beispiel führt die erste Iteration der foreach-Schleife dazu, dass die Ausführung solange in der Iteratormethode SomeNumbers fortschreitet, bis der ersten yield return-Anweisung erreicht wird.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. Diese Iteration gibt den Wert 3 zurück, und die aktuelle Postion in der Iteratormethode wird beibehalten.This iteration returns a value of 3, and the current location in the iterator method is retained. In der nächsten Iteration der Schleife wird die Ausführung in der Iteratormethode da fortgesetzt, wo sie beendet wurde, und endet dann wieder an einem yield return-Ausdruck.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. Diese Iteration gibt den Wert 5 zurück, und die aktuelle Postion in der Iteratormethode wird beibehalten.This iteration returns a value of 5, and the current location in the iterator method is again retained. Die Schleife wird beendet, wenn das Ende der Iteratormethode erreicht wird.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;  
}  

Der Rückgabetyp einer Iteratormethode oder einer get-Zugriffsmethode kann IEnumerable, IEnumerable<T>, IEnumerator oder IEnumerator<T> sein.The return type of an iterator method or get accessor can be IEnumerable, IEnumerable<T>, IEnumerator, or IEnumerator<T>.

Sie verwenden eine yield break-Anweisung, um die Iteration zu beenden.You can use a yield break statement to end the iteration.

Iteratoren wurden in C# in Visual Studio 2005 eingeführt.Iterators were introduced in C# in Visual Studio 2005.

InhaltIn this topic

Hinweis

Verwenden Sie in allen Beispielen dieses Themas, außer Einfacher Iterator, die Direktiven using für die Namespaces System.Collections und System.Collections.Generic.For all examples in this topic except the Simple Iterator example, include using directives for the System.Collections and System.Collections.Generic namespaces.

Einfacher IteratorSimple Iterator

In folgendem Beispiel wird eine yield return-Anweisung verwendet, die sich innerhalb einer for-Schleife befindet.The following example has a single yield return statement that is inside a for loop. In der Main-Methode erstellt jede Iteration des foreach-Anweisungstexts einen Aufruf der Iteratorfunktion, die zur nächsten yield return-Anweisung übergeht.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;  
        }  
    }  
}  

Erstellen einer AuflistungsklasseCreating a Collection Class

In folgendem Beispiel implementiert die DaysOfTheWeek-Klasse die IEnumerable-Schnittstelle, die eine GetEnumerator-Methode erfordert.In the following example, the DaysOfTheWeek class implements the IEnumerable interface, which requires a GetEnumerator method. Der Compiler ruft die Methode GetEnumerator implizit auf, die IEnumerator zurückgibt.The compiler implicitly calls the GetEnumerator method, which returns an IEnumerator.

Die Methode GetEnumerator gibt jede Zeichenfolge einzeln nacheinander mithilfe der Anweisung yield return zurück.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];  
        }  
    }  
}  

Im folgenden Beispiel wird eine Zoo-Klasse erstellt, die eine Auflistung von Tieren enthält.The following example creates a Zoo class that contains a collection of animals.

Die Anweisung foreach, die auf die Instanz der Klasse verweist (theZoo), ruft die Methode GetEnumerator implizit auf.The foreach statement that refers to the class instance (theZoo) implicitly calls the GetEnumerator method. Die Anweisung foreach, die auf die Eigenschaften Birds und Mammals verweist, verwenden die Iteratormethode AnimalsForType.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; }  
    }  
}  

Verwenden von Iteratoren mit einer generischen ListeUsing Iterators with a Generic List

In folgendem Beispiel implementiert die generische Klasse Stack(Of T) die generische Schnittstelle IEnumerable<T>.In the following example, the Stack(Of T) generic class implements the IEnumerable<T> generic interface. Die Methode Push weist Werte an Arrays des Typs T zu.The Push method assigns values to an array of type T. Die GetEnumerator-Methode gibt die Arraywerte mit der yield return-Anweisung zurück.The GetEnumerator method returns the array values by using the yield return statement.

Zusätzlich zur generischen Methode GetEnumerator muss auch die nicht generische Methode GetEnumerator implementiert werden.In addition to the generic GetEnumerator method, the non-generic GetEnumerator method must also be implemented. Dies liegt daran, dass IEnumerable<T> von IEnumerable erbt.This is because IEnumerable<T> inherits from IEnumerable. Die nicht generische Implementierung verzögert die generische Implementierung.The non-generic implementation defers to the generic implementation.

In diesem Beispiel werden benannte Iteratoren verwendet, um verschiedene Arten der Iteration in der selben Datenauflistung zu unterstützen.The example uses named iterators to support various ways of iterating through the same collection of data. Diese benannten Iteratoren sind die Eigenschaften TopToBottom und BottomToTop und die Methode TopN.These named iterators are the TopToBottom and BottomToTop properties, and the TopN method.

Die Eigenschaft BottomToTop verwendet einen Iterator in einer get-Zugriffsmethode.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];  
        }  
    }  

}  

SyntaxinformationenSyntax Information

Ein Iterator kann als Methode oder als get-Zugriffsmethode vorkommen.An iterator can occur as a method or get accessor. Ein Iterator kann nicht in einem Ereignis, Instanzenkonstruktor, statischen Konstruktor oder statischen Finalizer vorkommen.An iterator cannot occur in an event, instance constructor, static constructor, or static finalizer.

Es muss eine implizite Konvertierung vom Typ des Ausdrucks in der yield return-Anweisung in den Rückgabetyp des Iterators vorhanden sein.An implicit conversion must exist from the expression type in the yield return statement to the return type of the iterator.

In C# kann eine Iteratormethode nicht die Parameter ref oder out aufweisen.In C#, an iterator method cannot have any ref or out parameters.

In C# ist „yield“ kein reserviertes Wort, und es hat nur besondere Bedeutung, wenn es vor den Schlüsselwörtern return und break verwendet wird.In C#, "yield" is not a reserved word and has special meaning only when it is used before a return or break keyword.

Technische ImplementierungTechnical Implementation

Auch wenn Sie einen Iterator als Methode schreiben, führt der Compiler für diesen eine Translation in eine geschachtelte Klasse durch, die tatsächlich ein Zustandsautomat ist.Although you write an iterator as a method, the compiler translates it into a nested class that is, in effect, a state machine. Diese Klasse überwacht die Position des Iterators solange, wie die foreach-Schleife im Clientcode weiter ausgeführt wird.This class keeps track of the position of the iterator as long the foreach loop in the client code continues.

Um zu sehen, was der Compiler macht, können Sie das Tool Ildasm.exe verwenden, um den Intermediate Language-Code von Microsoft anzuzeigen, der für eine Iteratormethode generiert wird.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.

Wenn Sie einen Iterator für eine Klasse oder Struktur erstellen, müssen Sie die gesamte IEnumerator-Schnittstelle implementieren.When you create an iterator for a class or struct, you don't have to implement the whole IEnumerator interface. Wenn der Compiler einer Iterator erkennt, generiert er automatisch die Methoden Current, MoveNext und Dispose der IEnumerator- und IEnumerator<T>-Schnittstelle.When the compiler detects the iterator, it automatically generates the Current, MoveNext, and Dispose methods of the IEnumerator or IEnumerator<T> interface.

In jeder aufeinanderfolgenden Iteration der foreach-Schleife (oder im direkten Aufruf von IEnumerator.MoveNext) setzt der nächste Iteratorcodetext den Prozess nach der letzten yield return-Anweisung fort.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. Er springt dann zur nächsten yield return-Anweisung weiter, bis das Ende des Iteratortexts erreicht ist, oder bis er auf eine yield break-Anweisung trifft.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.

Iteratoren unterstützen die IEnumerator.Reset-Methode nicht.Iterators don't support the IEnumerator.Reset method. Um den Prozess erneut von Anfang an zu durchlaufen, müssen Sie einen neuen Iterator erstellen.To re-iterate from the start, you must obtain a new iterator.

Weitere Informationen finden Sie unter C#-Programmiersprachenspezifikation.For additional information, see the C# Language Specification.

Verwendung von IteratorenUse of Iterators

Mit Iteratoren können Sie die Einfachheit einer foreach-Schleife beibehalten, wenn Sie komplexen Code verwenden müssen, um eine Listensequenz aufzufüllen.Iterators enable you to maintain the simplicity of a foreach loop when you need to use complex code to populate a list sequence. Das kann für Folgende Aktionen nützlich sein:This can be useful when you want to do the following:

  • Das Modifizieren der Listensequenz nach der ersten Iteration einer foreach-Schleife.Modify the list sequence after the first foreach loop iteration.

  • Das Vermeiden des kompletten Ladens einer großen Liste vor der ersten Iteration einer foreach-Schleife.Avoid fully loading a large list before the first iteration of a foreach loop. Ein Beispiel dafür ist das ausgelagerte Abrufen, um einen Batch von Tabellenzeilen zu laden.An example is a paged fetch to load a batch of table rows. Ein anderes Beispiel ist die EnumerateFiles-Methode, die Iteratoren im .NET Framework implementiert.Another example is the EnumerateFiles method, which implements iterators within the .NET Framework.

  • Das Einschließen des Erstellens der Liste im Iterator.Encapsulate building the list in the iterator. In der Iteratormethode können Sie die Liste erstellen und anschließend jedes Ergebnis in eine Schleife liefern.In the iterator method, you can build the list and then yield each result in a loop.

Siehe auchSee Also

System.Collections.Generic
IEnumerable<T>
foreach, inforeach, in
yieldyield
Verwenden von foreach mit ArraysUsing foreach with Arrays
GenerikaGenerics