Implementieren eigener Iteratoren Iterator & yield (Teil 2)
Kommentare

Iteratoren leicht gemacht mit yield
Bisher wurde gezeigt, wie die Schnittstellen IEnumerable und IEnumerator selbst implementiert werden können. Der Aufwand ist dabei nicht zu vernachlässigen, vor

Iteratoren leicht gemacht mit yield

Bisher wurde gezeigt, wie die Schnittstellen IEnumerable und IEnumerator selbst implementiert werden können. Der Aufwand ist dabei nicht zu vernachlässigen, vor allem da dieser bei jeder eigenen Iterator-Klasse wiederholt werden muss. Viel einfacher geht es jedoch mit dem Schlüsselwort yield. Das yield Schlüsselwort vereinfacht die Umsetzung von Iteratorklassen erheblich, wie es Listing 4 demonstriert. Die Iteratorklasse in Listing 4 ist funktional gleichwertig zu der in Listing 1 gezeigten Iteratorumsetzung. Der Implementierungsaufwand hat sich jedoch drastisch reduziert. Lediglich die Methode GetEnumerator ist noch notwendig. Zu beachten ist auch, dass keine Schnittstelle mehr implementiert werden muss. Das heißt es können sogar mehrere Methoden ein IEnumerator zurückgeben und die Methodennamen sind dabei frei wählbar. So könnten z. B. Iteratormethoden definiert werden, die unterschiedliche Datenmengen zurückgeben. Zum Beispiel könnte eine Methode GetContactsByA realisiert werden, die alle Kontakte zurückgibt, deren Name mit A beginnt.

Listing 4

public class Contacts 
{
  private Contact[] contacts = null;
  public Contacts(Contact[] contacts)
  {
    this.contacts = new Contact[contacts.Length];
    int index = 0;
    foreach (var item in contacts)
      this.contacts[index++] = item;
  }
  public IEnumerator GetEnumerator()
  {
    for (int i = 0; i < contacts.Length; i++)
      yield return contacts[i];
  }
}  

Funktionsweise von yield

Das Schlüsselwort yield ermöglicht die einfache Umsetzung von eigenen Iteratoren. Hierbei führt es aber kein neues Sprachmittel ein, sondern es handelt sich hierbei eher um eine Compiler-Anweisung. Trifft der Compiler auf das Schlüsselwort yield, wird automatisch durch den Compiler eine vollwertige Iteratorklasse generiert und hinzugefügt. Listing 5 zeigt dazu ein einfaches Beispiel und in Listing 6 ist der korrespondierende Code, der durch den Compiler hinzugefügt wurde (gekürzt und aufbereitet), dargestellt. Der Compiler fügt eine neue Klasse ein, die die Schnittstellen IEnumerable

, IEnumerable, IEnumerator und IEnumerator implementiert. Innerhalb der generierten Klassen werden alle Methoden der Schnittstellen implementiert. Wie im Vergleich zu Listing 1 erkennbar wird, ergänzt der Compiler die fehlende Implementierung selbstständig und erstellt eine vollwertige Iteratorklasse. Beim Aufruf der originalen GetEnumerator-Methode wird nun eine Instanz der automatisch generierten Klasse zurückgegeben, die somit als Iteratorklasse eingesetzt wird. Aus der generierten Methode MoveNext wird die Verwaltung der Positionierung auf die Elemente deutlich. Die interne Variable 1_state hält den aktuellen Index. Wird MoveNext aufgerufen, wird die Variable 2_current auf das nächste Element gesetzt und der Index (1_state) aktualisiert. Ist kein folgendes Element mehr verfügbar, wird die Variable 1_state auf -1 gesetzt und die Methode MoveNext signalisiert das Ende über den bool-Rückgabewert false. Das hier gezeigte Beispiel verwendete zwei hart codierte yield-Anweisungen in der Ausgangsmethode GetEnumerable. Werden dynamische Daten verarbeitet, wird durch den Compiler ein etwas komplexerer Code generiert. Die generelle Funktionsweise bleibt aber - wie erläutert - bestehen. Listing 5
public class SimpleYield
{
  public IEnumerable GetEnumerable ()
  {
    yield return "Hello";
    yield return "World";
  }
}  
Listing 6

Zusammenfassung

Iteratoren ermöglichen den schnellen Zugriff auf Ergebnisse bevor alle Ergebnisse verfügbar sind. Für die Realisierung eigener iterationsfähiger Klassen können die genannten Schnittstellen eingesetzt werden. Wie aber auch gezeigt wurde, erleichtert und vereinfacht das Schlüsselwort yield die Implementierung von Iteratoren erheblich. Dabei darf aber nicht vergessen werden, dass durch das Schlüsselwort yield der Compiler im Hintergrund eine Menge Code erzeugt. Weitere Informationen und Hinweise für den richtigen Einsatz von Iteratoren sind auf MSDN erhältlich.

Marc André Zhou ist als freiberuflicher .NET-Technologieberater tätig. Seine Schwerpunkte sind .NET-Entwicklung, SharePoint, Softwarearchitekturen und Frameworks. Sie erreichen ihn unter zhou@dev-sky.net.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -