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#-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)
Yield-Anweisung (Visual Basic)
Verwenden von foreach mit Arrays (C#-Programmierhandbuch)