Freigeben über


Iteratoren (C# und Visual Basic)

Ein Iterator kann verwendet werden, um über Auflistungen wie Listen und Arrays zu durchlaufen.

Eine Iteratormethode oder ein get Accessor führt eine benutzerdefinierte Iteration über einer Auflistung aus. Eine Iteratormethode verwendet die - Anweisung Yield (Visual Basic) oder Rendite (C#), um jedes Element einzeln zurückzugeben. Wenn eine Yield oder yield return-Anweisung erreicht ist, wird an die aktuelle Position im Code gespeichert. Die Ausführung von diesem Speicherort neu gestartet, beim nächsten Öffnen der Iteratorfunktion aufgerufen wird.

Sie verwenden einen Iterator vom Clientcode, indem Sie eine - Anweisung Für jedes... Next-Schleife (Visual Basic) oder foreach (C#) oder indem Sie eine LINQ-Abfrage verwenden.

Im folgenden Beispiel wird die erste Iteration der Schleife For Each oder foreach Ausführung, in die SomeNumbers Iteratormethode bis erste Yield fortzusetzen, oder yield return-Anweisung erreicht wird. Diese Iteration gibt den Wert 3 zurück, und die aktuelle Position in der Iteratormethode wird beibehalten. Klicken Sie auf der nächsten Iteration der Schleife, wird die Ausführung in der Iteratormethode von fort, wo diese durch unterzogen und daraufhin erneut, wenn sie eine Yield oder yield return-Anweisung erreicht. Diese Iteration gibt den Wert 5 zurück, und die aktuelle Position in der Iteratormethode wird erneut beibehalten. Die Schleife wird beendet, wenn das Ende der Iteratormethode erreicht wird.

Sub Main()
    For Each number As Integer In SomeNumbers()
        Console.Write(number & " ")
    Next 
    ' Output: 3 5 8
    Console.ReadKey()
End Sub 

Private Iterator Function SomeNumbers() As System.Collections.IEnumerable
    Yield 3
    Yield 5
    Yield 8
End Function
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 des get Accessor kann IEnumerable, IEnumerable, IEnumerator oder IEnumerator sein.

Sie können eine Exit Function oder Return-Anweisung (Visual Basic) oder yield break-Anweisung (C#) die Iteration zu beenden.

Eine Visual Basic Iteratorfunktion oder eine get Accessordeklaration umfasst einen Iterator-Modifizierer.

Iteratoren wurden in C# in Visual Studio 2005 eingegeben und wurden in Visual Basic in Visual Studio 2012 eingeführt.

In diesem Thema

  • Einfacher Iterator

  • Erstellen einer Auflistungsklasse

  • Try-Blöcke in Visual Basic

  • Anonyme Methoden in Visual Basic

  • Verwenden von Iteratoren mit einer generischen Liste

  • Syntax-Informationen

  • Technische Implementierung

  • Verwendung von Iteratoren

Einfacher Iterator

Im folgenden Beispiel verfügt yield return einzelnes Yield oder eine Anweisung, die in einer Schleife Für... Next-Schleife (Visual Basic) oder für (C#) ist. In Main erstellt jede Iteration des For Each oder foreach-Anweisungstexts einen Aufruf der Iteratorfunktion, die zu folgenden Yield oder zur yield return-Anweisung übergeht.

Sub Main()
    For Each number As Integer In EvenSequence(5, 18)
        Console.Write(number & " ")
    Next 
    ' Output: 6 8 10 12 14 16 18
    Console.ReadKey()
End Sub 

Private Iterator Function EvenSequence(
ByVal firstNumber As Integer, ByVal lastNumber As Integer) _
As System.Collections.Generic.IEnumerable(Of Integer)

    ' Yield even numbers in the range. 
    For number As Integer = firstNumber To lastNumber
        If number Mod 2 = 0 Then
            Yield number
        End If 
    Next 
End Function
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 Auflistungsklasse

Hinweis

Für die verbleibenden Beispiele im Thema schließen Sie Imports-Anweisungen (Visual Basic) oder using-Direktiven (C#) für die System.Collections- und System.Collections.Generic-Namespaces ein.

Im folgenden Beispiel DaysOfTheWeek implementiert die Klasse die IEnumerable-Schnittstelle, die eine GetEnumerator-Methode erfordert. Der Compiler ruft implizit die GetEnumerator-Methode, die IEnumerator zurückgibt.

Die GetEnumerator-Methode gibt jede Zeichenfolge einzeln zurück, indem sie die Yield oder yield return-Anweisung verwendet. Im Visual Basic Code ist ein Iterator-Modifizierer in der Funktionsdeklaration.

Sub Main()
    Dim days As New DaysOfTheWeek()
    For Each day As String In days
        Console.Write(day & " ")
    Next 
    ' Output: Sun Mon Tue Wed Thu Fri Sat
    Console.ReadKey()
End Sub 

Private Class DaysOfTheWeek
    Implements IEnumerable

    Public days =
        New String() {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}

    Public Iterator Function GetEnumerator() As IEnumerator _
        Implements IEnumerable.GetEnumerator

        ' Yield each day of the week. 
        For i As Integer = 0 To days.Length - 1
            Yield days(i)
        Next 
    End Function 
End Class
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, die eine Auflistung Tiere enthält.

Die For Each oder foreach-Anweisung, die die Klasseninstanz (theZoo) implizit verweist, GetEnumerator ruft die Methode auf. Die For Each oder foreach-Anweisungen, die die Birds und Mammals-Eigenschaften verweisen, verwenden AnimalsForType, das Iteratormethode genannt wird.

Sub Main()
    Dim theZoo As New Zoo()

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

    For Each name As String In theZoo
        Console.Write(name & " ")
    Next
    Console.WriteLine()
    ' Output: Whale Rhinoceros Penguin Warbler 

    For Each name As String In theZoo.Birds
        Console.Write(name & " ")
    Next
    Console.WriteLine()
    ' Output: Penguin Warbler 

    For Each name As String In theZoo.Mammals
        Console.Write(name & " ")
    Next
    Console.WriteLine()
    ' Output: Whale Rhinoceros

    Console.ReadKey()
End Sub 

Public Class Zoo
    Implements IEnumerable

    ' Private members. 
    Private animals As New List(Of Animal)

    ' Public methods. 
    Public Sub AddMammal(ByVal name As String)
        animals.Add(New Animal With {.Name = name, .Type = Animal.TypeEnum.Mammal})
    End Sub 

    Public Sub AddBird(ByVal name As String)
        animals.Add(New Animal With {.Name = name, .Type = Animal.TypeEnum.Bird})
    End Sub 

    Public Iterator Function GetEnumerator() As IEnumerator _
        Implements IEnumerable.GetEnumerator

        For Each theAnimal As Animal In animals
            Yield theAnimal.Name
        Next 
    End Function 

    ' Public members. 
    Public ReadOnly Property Mammals As IEnumerable
        Get 
            Return AnimalsForType(Animal.TypeEnum.Mammal)
        End Get 
    End Property 

    Public ReadOnly Property Birds As IEnumerable
        Get 
            Return AnimalsForType(Animal.TypeEnum.Bird)
        End Get 
    End Property 

    ' Private methods. 
    Private Iterator Function AnimalsForType( _
    ByVal type As Animal.TypeEnum) As IEnumerable
        For Each theAnimal As Animal In animals
            If (theAnimal.Type = type) Then
                Yield theAnimal.Name
            End If 
        Next 
    End Function 

    ' Private class. 
    Private Class Animal
        Public Enum TypeEnum
            Bird
            Mammal
        End Enum 

        Public Property Name As String 
        Public Property Type As TypeEnum
    End Class 
End Class
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; }
    }
}

Try-Blöcke in Visual Basic

Visual Basic ermöglicht eine Yield-Anweisung im Try-Block von Try...Catch...Finally-Anweisung (Visual Basic). Ein Try-Block, der eine Yield-Anweisung hat, kann Catch Blöcke haben und kann einen Finally-Block haben.

C#-HinweisC#-Hinweis:

C# ermöglicht eine yield return-Anweisung im try-Block einer try-finally-Anweisung-Anweisung.Ein try-Block, der eine yield return-Anweisung hat, kann keine catch Blöcke haben.

Im folgenden Beispiel Visual Basic umfasst Try, Catch, und Finally Blöcke in einem Iterator arbeiten. Der Finally-Block in der Iteratorfunktion führt vor der For Each Iterationsende aus.

Sub Main()
    For Each number As Integer In Test()
        Console.WriteLine(number)
    Next
    Console.WriteLine("For Each is done.")

    ' Output: 
    '  3 
    '  4 
    '  Something happened. Yields are done. 
    '  Finally is called. 
    '  For Each is done.
    Console.ReadKey()
End Sub 

Private Iterator Function Test() As IEnumerable(Of Integer)
    Try
        Yield 3
        Yield 4
        Throw New Exception("Something happened. Yields are done.")
        Yield 5
        Yield 6
    Catch ex As Exception
        Console.WriteLine(ex.Message)
    Finally
        Console.WriteLine("Finally is called.")
    End Try 
End Function

Eine Yield-Anweisung kann nicht innerhalb eines Catch-Blocks oder eines - Blocks Finally sein.

Wenn der For Each Text (anstelle der Iteratormethode) eine Ausnahme auslöst, wird ein Catch-Block in der Iteratorfunktion nicht ausgeführt, aber ein Finally-Block in der Iteratorfunktion wird ausgeführt. Ein Catch-Block in einer Iteratorfunktion fängt nur Ausnahmen ab, die in der Iteratorfunktion auftreten.

Anonyme Methoden in Visual Basic

In Visual Basic (jedoch nicht in C#), können anonyme Funktion eine Iteratorfunktion sein. Dies wird anhand des folgenden Beispiels veranschaulicht:

Dim iterateSequence = Iterator Function() _
                      As IEnumerable(Of Integer)
                          Yield 1
                          Yield 2
                      End Function 

For Each number As Integer In iterateSequence()
    Console.Write(number & " ")
Next 
' Output: 1 2
Console.ReadKey()

Im folgenden Beispiel Visual Basic verfügt über eine NichtIteratormethode, die die Argumente überprüft. Die - Methode gibt das Ergebnis eines anonymen Iterators zurück, der die Auflistungselemente beschreibt.

Sub Main()
    For Each number As Integer In GetSequence(5, 10)
        Console.Write(number & " ")
    Next 
    ' Output: 5 6 7 8 9 10
    Console.ReadKey()
End Sub 

Public Function GetSequence(ByVal low As Integer, ByVal high As Integer) _
As IEnumerable
    ' Validate the arguments. 
    If low < 1 Then 
        Throw New ArgumentException("low is too low")
    End If 
    If high > 140 Then 
        Throw New ArgumentException("high is too high")
    End If 

    ' Return an anonymous iterator function. 
    Dim iterateSequence = Iterator Function() As IEnumerable
                              For index = low To high
                                  Yield index
                              Next 
                          End Function 
    Return iterateSequence()
End Function

Wenn die Validierung stattdessen in der Iteratorfunktion ist, kann die Validierung nicht bis zum Anfang der ersten Iteration des For Each Text ausgeführt werden.

Verwenden von Iteratoren mit einer generischen Liste

Im folgenden Beispiel implementiert die generische Klasse Stack(Of T) die generische - Schnittstelle IEnumerable. Die - Methode weist Push-Werte an ein Array vom Typ T zu. Die - Methode gibt die GetEnumerator Arraywerte zurück, indem sie die Yield oder yield return-Anweisung verwendet.

Neben der generischen Methode GetEnumerator muss die nicht generische GetEnumerator-Methode auch implementiert werden. Dies ist, da IEnumerable von IEnumerable erbt. Die nicht generische Implementierung verzögert auf generische Implementierung.

Das Beispiel verwendet benannte Iteratoren, um verschiedene Methoden über das Durchlaufen über dieselbe Datenerfassung zu unterstützen. Diese benannten Iteratoren sind die TopToBottom und BottomToTop-Eigenschaften und die TopN-Methode.

Die BottomToTop-Eigenschaft verwendet einen Iterator in einem get Accessor. Im Visual Basic Code umfasst die Eigenschaftendeklaration Iterator das Schlüsselwort.

Sub Main()
    Dim theStack As New Stack(Of Integer)

    ' Add items to the stack. 
    For number As Integer = 0 To 9
        theStack.Push(number)
    Next 

    ' Retrieve items from the stack. 
    ' For Each is allowed because theStack implements 
    ' IEnumerable(Of Integer). 
    For Each number As Integer In theStack
        Console.Write("{0} ", number)
    Next
    Console.WriteLine()
    ' Output: 9 8 7 6 5 4 3 2 1 0 

    ' For Each is allowed, because theStack.TopToBottom 
    ' returns IEnumerable(Of Integer). 
    For Each number As Integer In theStack.TopToBottom
        Console.Write("{0} ", number)
    Next
    Console.WriteLine()
    ' Output: 9 8 7 6 5 4 3 2 1 0 

    For Each number As Integer In theStack.BottomToTop
        Console.Write("{0} ", number)
    Next
    Console.WriteLine()
    ' Output: 0 1 2 3 4 5 6 7 8 9  

    For Each number As Integer In theStack.TopN(7)
        Console.Write("{0} ", number)
    Next
    Console.WriteLine()
    ' Output: 9 8 7 6 5 4 3

    Console.ReadKey()
End Sub 

Public Class Stack(Of T)
    Implements IEnumerable(Of T)

    Private values As T() = New T(99) {}
    Private top As Integer = 0

    Public Sub Push(ByVal t As T)
        values(top) = t
        top = top + 1
    End Sub 

    Public Function Pop() As T
        top = top - 1
        Return values(top)
    End Function 

    ' This function implements the GetEnumerator method. It allows 
    ' an instance of the class to be used in a For Each statement. 
    Public Iterator Function GetEnumerator() As IEnumerator(Of T) _
        Implements IEnumerable(Of T).GetEnumerator

        For index As Integer = top - 1 To 0 Step -1
            Yield values(index)
        Next 
    End Function 

    Public Iterator Function GetEnumerator1() As IEnumerator _
        Implements IEnumerable.GetEnumerator

        Yield GetEnumerator()
    End Function 

    Public ReadOnly Property TopToBottom() As IEnumerable(Of T)
        Get 
            Return Me 
        End Get 
    End Property 

    Public ReadOnly Iterator Property BottomToTop As IEnumerable(Of T)
        Get 
            For index As Integer = 0 To top - 1
                Yield values(index)
            Next 
        End Get 
    End Property 

    Public Iterator Function TopN(ByVal itemsFromTop As Integer) _
        As IEnumerable(Of T)

        ' Return less than itemsFromTop if necessary. 
        Dim startIndex As Integer =
            If(itemsFromTop >= top, 0, top - itemsFromTop)

        For index As Integer = top - 1 To startIndex Step -1
            Yield values(index)
        Next 
    End Function 
End Class
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-Informationen

Ein Iterator kann als Methode oder Accessor get auftreten. Ein Iterator kann nicht in einem Ereignis, in einem Instanzenkonstruktor, in einem statischen Konstruktor oder in einem statischen Destruktor auftreten.

Eine implizite Konvertierung muss vom Ausdruck sind in der - Anweisung Yield (Visual Basic) oder yield return (C#) in den Rückgabetyp des Iterators.

In Visual Basic kann eine Iteratormethode keine ByRef-Parameter haben. In C# kann eine Iteratormethode keine ref oder out-Parameter haben.

In Visual Basic"Yield" ist kein reserviertes Wort und ist eine besondere Bedeutung, falls dies in einer Iterator-Methode oder einem get Accessor verwendet wird. In C# "Yield" ist kein reserviertes Wort und ist eine besondere Bedeutung, falls dies vor einem return oder break-Schlüsselwort verwendet wird.

Technische Implementierung

Obwohl ein Iterator als Methode geschrieben wird, wird sie vom Compiler in eine geschachtelte Klasse übersetzt, die tatsächlich einem Zustandsautomaten entspricht. Diese Klasse registriert die Position des Iterators, solange die For Each...Next- oder foreach-Schleife im Clientcode fortgesetzt wird.

Um zu sehen, was der Compiler führt können Sie das Ildasm.exe-Tool verwenden um den Microsoft Intermediate Language-Code anzuzeigen der für eine Iteratormethode generiert wird.

Wenn Sie einen Iterator für - Klasse oder Struktur erstellen, müssen Sie die gesamte IEnumerator-Schnittstelle nicht implementieren. Wenn der Compiler den Iterator erkennt, generiert er automatisch Current, MoveNext und Dispose-Methoden der IEnumerator oder IEnumerator-Schnittstelle.

Klicken Sie in aufeinander folgenden Iterationen der Schleife For Each…Next oder foreach (oder des direkten Aufruf zum IEnumerator.MoveNext), den folgenden Iteratorcodetextzusammenfassungen nach vorherigen Yield oder der yield return-Anweisung. Er fährt dann zu folgenden Yield oder zur yield return-Anweisung fortgesetzt, bis das Ende des Iteratortexts erreicht ist, oder bis eine Exit Function oder Return-Anweisung (Visual Basic) oder yield break-Anweisung (C#) auftritt.

Iteratoren unterstützen die IEnumerator.Reset-Methode nicht. Um von Anfang an zu überprüfen, müssen Sie sich ein neuer Iterator.

Weitere Informationen finden Sie unter Visual Basic-Sprachspezifikation oder C#-Sprachspezifikation.

Verwendung von Iteratoren

Iteratoren ermöglichen Ihnen, die Einfachheit einer For Each-Schleife beibehalten, wenn Sie komplexen Code verwenden müssen, um eine Listensequenz aufzufüllen. Dies kann hilfreich sein, wenn Sie Folgendes durchführen möchten:

  • Ändern Sie die Listensequenz nach der ersten Schleifeniteration For Each.

  • Vermeiden Sie es, eine große Liste vollständig zu laden vor der ersten Iteration einer Schleife For Each. Ein Beispiel ist ein ausgelagerter Abrufen, um einen Batch Tabellenzeilen zu laden. Ein weiteres Beispiel ist die Methode, die EnumerateFiles Iteratoren innerhalb von .NET Framework implementiert.

  • Kapseln Sie das Erstellen der Liste im Iterator. In der Iteratormethode können Sie die Liste erstellen und jedes Ergebnis in einer Schleife dann führen.

Die folgenden C#-Blogs enthalten weitere Informationen über die Verwendung von Iteratoren.

Siehe auch

Referenz

For Each...Next-Anweisung (Visual Basic)

foreach, in (C#-Referenz)

Yield-Anweisung (Visual Basic)

yield (C#-Referenz)

Iterator (Visual Basic)

Verwenden von foreach mit Arrays (C#-Programmierhandbuch)

Generika (C#-Programmierhandbuch)

System.Collections.Generic

IEnumerable