8.6 Eigene Auflistungen mit »yield« durchlaufen
Nehmen wir an, wir hätten eine Klassendefinition wie folgt:
public class Months {
string[] months = { "Januar", "Februar", "März", "April",
"Mai", "Juni", "Juli", "August",
"September", "Oktober", "November", "Dezember"
};
}
Listing 8.22 Definition der Klasse »Months«
Wäre es nicht schön, mit einer foreach-Schleife den Datenspeicher des Objekts months zu durchlaufen und Zugriff auf alle Elemente zu erhalten, etwa wie folgt:
Months monate = new Months();
foreach(string temp in monate) {
Console.WriteLine(temp);
}
Listing 8.23 Ein »Months«-Objekt in einer foreach-Schleife durchlaufen
Dass daran Bedingungen geknüpft sind, wurde weiter oben schon erwähnt. Die Klasse Months muss dazu die Schnittstelle IEnumerable implementieren.
public class Months : IEnumerable
Die einzige in IEnumerable definierte Methode GetEnumerator liefert ein Objekt, das wiederum die Schnittstelle IEnumerator unterstützt.
Das von der Methode GetEnumerator zurückgelieferte IEnumerator-Objekt muss die Methoden MoveNext und Reset sowie die Eigenschaft Current implementieren, damit das Durchlaufen des Objekts mit foreach möglich wird.
MoveNext positioniert den Enumerator nach dem ersten Aufruf vor das erste Element der Auflistung und setzt den Positionszeiger mit jedem Aufruf auf das nächste Element in der Liste. Gleichzeitig wird ein boolescher Wert zurückgeliefert, der true ist, wenn der Enumerator auf ein Element gesetzt werden konnte, und false, falls der Enumerator das Ende der Liste überschritten hat. Die Methode Reset setzt den Positionszeiger auf die Position vor dem ersten Element der Liste, und Current ruft das aktuelle Element ab.
Die Beschreibung macht deutlich, dass einiges an Tippaufwand erforderlich ist, um aus einer Klasse wie Months eine Liste zu machen, die in einer foreach-Schleife durchlaufen werden kann.
Durch den Einsatz des Schlüsselwortes yield geht es aber auch einfacher. Sie müssen zwar immer noch die Schnittstelle IEnumerable und damit auch die Methode GetEnumerator implementieren, benötigen aber keinen IEnumerator-Typ mehr. Stattdessen liefern Sie die Daten nur noch mit dem neuen Schlüsselwort yield, gefolgt von return, aus.
// Beispiel: ..\Kapitel 8\YieldSample
class Program {
static void Main(string[] args) {
Months months = new Months();
foreach(string temp in months)
Console.WriteLine(temp);
Console.ReadLine();
}
}
public class Months : IEnumerable
{
string[] month = { "Januar", "Februar", "März", "April",
"Mai", "Juni", "Juli", "August", "September",
"Oktober", "November", "Dezember"
};
// Methode der Schnittstelle IEnumerable
public IEnumerator GetEnumerator() {
for (int i = 0; i < month.Length; i++)
yield return month[i];
}
}
Listing 8.24 Klasse, die in einer foreach-Schleife durchlaufen werden kann
yield in Kombination mit return wird zur Angabe des zurückgegebenen Wertes verwendet. Bei Erreichen von yield return wird die aktuelle Position gespeichert, und beim nächsten Aufruf der Schleife wird die Ausführung von dieser Position neu gestartet. Mehr haben Sie nicht zu tun, denn im Hintergrund generiert der Compiler automatisch die Methoden Current und MoveNext der IEnumerator-Schnittstelle, wenn er yield erkennt.
Sie können das Programm sogar noch einfacher schreiben und auf die Implementierung von IEnumerable verzichten. Überlassen Sie einfach alles dem Compiler und yield return. Dazu schreiben Sie ebenfalls eine Methode, deren spezielle Aufgabe es ist, die Objektmenge zurückzuliefern. Die Methode dürfen Sie beliebig benennen. Der Rückgabewert ist ein Objekt, das die Schnittstelle IEnumerable implementiert – und somit auch implizit die Methode GetEnumerator. Hinter den Kulissen wird der Compiler dafür sorgen, dass der Iterator der anfragenden foreach-Schleife alle Daten der Reihe nach übergibt.
Das folgende Listing zeigt, wie einfach jetzt der Code ist. Beachten Sie bitte auch, dass in der foreach-Schleife nun die Methode GetList für die Bereitstellung der Objekte sorgt.
class Program {
static void Main(string[] args) {
Months months = new Months();
foreach(string temp in months.GetList())
Console.WriteLine(temp);
Console.ReadLine();
}
}
public class Months
{
string[] month = { [...] };
public IEnumerable GetList() {
for (int i = 0; i < month.Length; i++)
yield return month[i];
}
}
Listing 8.25 Klasse ohne das Interface »IEnumerable«
Einschränkungen von »yield return«
Der Einsatz von yield return unterliegt zwei Einschränkungen:
- yield return kann nicht innerhalb einer anonymen Methode codiert werden.
- yield return darf weder in einem catch-Block noch in einem try-Block verwendet werden, wenn Letzterer eine catch-Klausel hat. Die Verwendung in einem try-Block, dem sich nur noch ein finally-Block anschließt, ist jedoch möglich.
Weitere Möglichkeiten
yield return ist für den Compiler der Anstoß, automatisch einen Iterator zu erzeugen, der von einer foreach-Schleife genutzt werden kann. Sie können auch mehrfach hintereinander yield aufrufen, wie das folgende Codefragment zeigt:
// Methode der Schnittstelle IEnumerable
public IEnumerator GetEnumerator() {
yield return "Januar";
yield return "Februar";
yield return "März";
}
Listing 8.26 Mehrere Aufrufe von »yield«
Es werden der Reihe nach die drei Monate ausgegeben.
In einem Iterator-Block ist das Statement return nicht zulässig. Zum Abbruch einer Iteration kombinieren Sie stattdessen yield mit break:
yield break;
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.