Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Ein Iterator kann zum Durchlaufen von Auflistungen wie Listen und Arrays verwendet werden.
Eine Iteratormethode oder eine get
-Zugriffsmethode führt eine benutzerdefinierte Iteration einer Auflistung durch. Eine Iterator-Methode verwendet die Renditerückgabe-Anweisung , um jedes Element einzeln zurückzugeben. Wenn eine yield return
-Anweisung erreicht wird, wird die aktuelle Position im Code gespeichert. Die Ausführung wird von diesem Speicherort neu gestartet, wenn die Iteratorfunktion das nächste Mal aufgerufen wird.
Sie erzeugen einen Iterator aus einem Clientcode, indem Sie eine foreach-Anweisung oder eine LINQ-Abfrage verwenden.
Im folgenden Beispiel bewirkt die erste Iteration der foreach
Schleife, dass die Ausführung in der SomeNumbers
Iteratormethode fortgesetzt wird, bis die erste yield return
Anweisung erreicht ist. Diese Iteration gibt den Wert 3 zurück, und die aktuelle Position in der Iteratormethode wird beibehalten. 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. Diese Iteration gibt den Wert 5 zurück, und die aktuelle Position in der Iteratormethode wird erneut beibehalten. Die Schleife wird abgeschlossen, wenn das Ende der Iteratormethode erreicht ist.
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 eines get
-Accessors kann IEnumerable, IEnumerable<T>, IEnumerator oder IEnumerator<T> sein.
Sie können eine yield break
Anweisung verwenden, um die Iteration zu beenden.
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
.
Einfacher Iterator
Das folgende Beispiel weist eine einzelne yield return
Anweisung auf, die sich in einer For-Schleife befindet. In Main
, erstellt jede Iteration des foreach
Anweisungstexts einen Aufruf der Iteratorfunktion, die mit der nächsten yield return
Anweisung fortfährt.
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 Sammlungsklasse
Im folgenden Beispiel implementiert die DaysOfTheWeek
Klasse die IEnumerable Schnittstelle, die eine GetEnumerator Methode erfordert. Der Compiler ruft die GetEnumerator
-Methode implizit auf, die eine IEnumerator zurückgibt.
Die GetEnumerator
Methode gibt jede Zeichenfolge jeweils einzeln mithilfe der yield return
Anweisung zurück.
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 Sammlung von Tieren enthält.
Die foreach
Anweisung, die auf die Klasseninstanz (theZoo
) verweist, ruft die GetEnumerator
Methode implizit auf. Die foreach
Anweisungen, die auf die Birds
- und Mammals
-Eigenschaften verweisen, verwenden die AnimalsForType
-genannte Iteratormethode.
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 Liste
Im folgenden Beispiel implementiert die Stack<T> generische Klasse die IEnumerable<T> generische Schnittstelle. Die Push Methode weist einem Array vom Typ T
Werte zu. Die GetEnumerator Methode gibt die Arraywerte mithilfe der yield return
Anweisung zurück.
Zusätzlich zur generischen GetEnumerator Methode muss auch die nicht generische GetEnumerator Methode implementiert werden. Dies liegt daran, dass IEnumerable<T> von IEnumerable erbt. Die nicht generische Implementierung verzögert die generische Implementierung.
Im Beispiel werden benannte Iteratoren verwendet, um verschiedene Möglichkeiten zum Durchlaufen derselben Sammlung von Daten zu unterstützen. Diese benannten Iteratoren sind die TopToBottom
und BottomToTop
Eigenschaften sowie die TopN
Methode.
Die Eigenschaft BottomToTop
verwendet einen Iterator in einer get
-Zugriffsmethode.
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];
}
}
}
Syntaxinformationen
Ein Iterator kann als Methode oder get
Accessor auftreten. Ein Iterator kann nicht in einem Ereignis, Instanzenkonstruktor, statischen Konstruktor oder statischen Finalizer vorkommen.
Eine implizite Konvertierung muss vom Ausdruckstyp in der yield return
-Anweisung zum Typ-Argument des durch den Iterator zurückgegebenen IEnumerable<T>
-Wertes vorhanden sein.
In C# kann eine Iteratormethode keine Parameter in
, ref
oder out
enthalten.
In C# handelt es sich nicht um ein reserviertes Wort und hat nur dann eine besondere Bedeutung, yield
wenn es vor einem return
Oder break
Schlüsselwort verwendet wird.
Technische Implementierung
Obwohl Sie einen Iterator als Methode schreiben, übersetzt der Compiler ihn in eine geschachtelte Klasse, die tatsächlich ein Zustandsautomat ist. Diese Klasse verfolgt die Position des Iterators, solange die foreach
Schleife im Clientcode fortgesetzt wird.
Um zu sehen, was der Compiler tut, können Sie das Ildasm.exe Tool verwenden, um den gängigen Zwischensprachencode anzuzeigen, der für eine Iteratormethode generiert wird.
Wenn Sie einen Iterator für eine Klasse oder Struktur erstellen, müssen Sie die gesamte IEnumerator Schnittstelle nicht implementieren. Wenn der Compiler den Iterator erkennt, generiert er automatisch die Current
, MoveNext
und Dispose
Methoden der IEnumerator oder IEnumerator<T> Schnittstelle.
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. Es wird dann zur nächsten yield return
Anweisung übergegangen, bis das Ende des Iterator-Körpers erreicht ist oder eine yield break
Anweisung auftritt.
Iteratoren unterstützen die IEnumerator.Reset Methode nicht. Wenn der Prozess erneut von Anfang an durchlaufen werden soll, müssen Sie einen neuen Iterator erstellen. Durch das Aufrufen von Reset für den von einer Iteratormethode zurückgegebenen Iterator wird NotSupportedException ausgelöst.
Weitere Informationen finden Sie in der C#-Sprachspezifikation.
Verwendung von Iteratoren
Iteratoren ermöglichen es Ihnen, die Einfachheit einer foreach
Schleife beizubehalten, wenn Sie komplexen Code zum Auffüllen einer Listensequenz verwenden müssen. Dies kann nützlich sein, wenn Sie Folgendes ausführen möchten:
Das Modifizieren der Listensequenz nach der ersten Iteration einer
foreach
-Schleife.Vermeiden Sie das vollständige Laden einer großen Liste vor der ersten Iteration einer
foreach
Schleife. Ein Beispiel ist ein seitenseitiger Abruf zum Laden eines Stapels von Tabellenzeilen. Ein weiteres Beispiel ist die EnumerateFiles Methode, die Iteratoren in .NET implementiert.Kapseln Sie das Erstellen der Liste im Iterator. In der Iteratormethode können Sie die Liste erstellen und dann jedes Ergebnis in einer Schleife liefern.