C# Iterator Pattern einfach erklärt: So implementierst du es
So implementierst du das Iterator-Pattern in C# mit IEnumerable und IEnumerator – sauberer Code für Azubis, Studenten und Berufseinsteiger.
Von Marcel Schmidtpeter 30. Sept. 2026
Stell dir vor, du baust im Ausbildungsprojekt eine Klasse `Kursverwaltung`, die ihre Daten intern in mehreren Arrays und Hilfslisten hält. Jetzt soll der Kollege aus der Abwicklung einfach mit `foreach` über alle Kurse laufen können - ohne dass er etwas über deine interne Struktur wissen muss. Genau dafür gibt es das C# Iterator Pattern, und C# hat es so tief in die Sprache eingebaut, dass du es täglich benutzt, ohne es zu merken. Zeit, den Mechanismus hinter `foreach` zu verstehen - egal ob du gerade eine IHK-Prüfung in Softwareentwicklung schreibst, Design Patterns in der Uni-Vorlesung hattest oder im ersten Job auf fremden Code triffst.
Was ist das Iterator Pattern?
Das Iterator Pattern ist ein Verhaltensmuster aus dem klassischen GoF-Katalog (Gang of Four). Es bietet einen standardisierten Weg, Elemente einer Sammlung nacheinander zu durchlaufen - ohne die interne Darstellung der Sammlung nach außen preiszugeben.
Die Analogie: Die Sammlung ist ein Buch, der Iterator ist dein Lesezeichen. Das Buch sagt dir nicht, wie es gedruckt wurde - du blätterst einfach Seite für Seite. Und du kannst mehrere Lesezeichen unabhängig voneinander im selben Buch verwenden.
Merke: Das Iterator Pattern trennt die Datenstruktur (Was wird durchlaufen?) vom Durchlaufen selbst (Wie wird iteriert?).
Wenn du die objektorientierten Grundlagen dazu auffrischen willst, wirf einen Blick in unseren Artikel zu den OOP-Prinzipien in C#.
Wie setzt C# das Iterator Pattern um?
In C# lebt das Pattern in den beiden Schnittstellen `IEnumerable<T>` ("Gib mir einen Iterator") und `IEnumerator<T>` ("Ich bin der Iterator"). Die generische Variante solltest du immer bevorzugen, weil du dann typsicher arbeitest.
`IEnumerable<T>` | `IEnumerator<T>` | |
Rolle | Die Sammlung, die durchlaufen werden kann | Der Cursor, der durchläuft |
Analogie | Das Buch | Das Lesezeichen |
Zentrale Member | `GetEnumerator()` | `MoveNext()`, `Current`, `Reset()` |
Die offizielle Referenz findest du auf Microsoft Learn zu IEnumerable<T> und IEnumerator<T>.
Was macht foreach eigentlich im Hintergrund?
`foreach` ist Syntaxzucker - der Compiler übersetzt es in Enumerator-Aufrufe. Das hier:
List<int> zahlen = new List<int> { 1, 2, 3 };
foreach (int zahl in zahlen)
{
Console.WriteLine(zahl);
}wird vom Compiler ungefähr so umgeschrieben:
List<int>.Enumerator e = zahlen.GetEnumerator();
while (e.MoveNext())
{
int zahl = e.Current;
Console.WriteLine(zahl);
}`MoveNext()` rückt den Cursor weiter und liefert `true`, solange Elemente übrig sind. `Current` gibt das Element an der aktuellen Position zurück. Streng genommen baut der Compiler zusätzlich noch ein `try`/`finally` mit `Dispose()` darum - dieses Detail kannst du in der Microsoft-Dokumentation nachlesen. Mehr dazu haben wir im Artikel zu IEnumerable und foreach in C#.
Merke: `foreach` ruft unter der Haube `GetEnumerator()`, `MoveNext()` und `Current` auf - und räumt am Ende mit `Dispose()` auf.
Wie implementierst du das C# Iterator Pattern selbst?
Deine Klasse implementiert `IEnumerable<T>` und entscheidet damit selbst, wie sie durchlaufen wird. Hier eine `Playlist`, die intern eine `List<Song>` nutzt - nach außen sieht davon niemand etwas:
using System;
using System.Collections;
using System.Collections.Generic;
public class Song
{
public string Titel { get; }
public int LaengeInSekunden { get; }
public Song(string titel, int laengeInSekunden)
{
Titel = titel;
LaengeInSekunden = laengeInSekunden;
}
}
public class Playlist : IEnumerable<Song>
{
private readonly List<Song> songs = new List<Song>();
public void Hinzufuegen(Song song)
{
songs.Add(song);
}
// Eigene Durchlaufreihenfolge: rueckwaerts
public IEnumerator<Song> GetEnumerator()
{
for (int i = songs.Count - 1; i >= 0; i--)
{
yield return songs[i];
}
}
// Nicht-generische Variante fuer die Kompatibilitaet
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}Die Nutzung fühlt sich dann völlig natürlich an:
var playlist = new Playlist();
playlist.Hinzufuegen(new Song("Bohemian Rhapsody", 354));
playlist.Hinzufuegen(new Song("Enter Sandman", 331));
playlist.Hinzufuegen(new Song("Smells Like Teen Spirit", 301));
foreach (Song song in playlist)
{
Console.WriteLine($"{song.Titel} - {song.LaengeInSekunden / 60}:{song.LaengeInSekunden % 60:00}");
}Was macht yield return genau?
`yield return` erzeugt den Iterator-Code automatisch. Statt eine eigene Klasse zu schreiben, die `MoveNext()` und `Current` implementiert, sagst du nur: "Hier ist das nächste Element." Der Compiler baut daraus eine versteckte Zustandsmaschine (State Machine).
Das Besondere: Der Code wird lazy ausgeführt, also erst dann, wenn wirklich ein Element angefordert wird. Damit kannst du sogar unendliche Folgen beschreiben:
public static IEnumerable<int> GeradeZahlen()
{
int zahl = 0;
while (true)
{
yield return zahl;
zahl += 2;
}
}
// Nutzung: nur die ersten 5 Elemente werden berechnet
foreach (int z in GeradeZahlen())
{
if (z > 8)
{
break;
}
Console.WriteLine(z); // 0, 2, 4, 6, 8
}Mit `yield break;` beendest du eine solche Folge vorzeitig. Die offizielle Dokumentation dazu gibt's auf Microsoft Learn zu yield.
Merke: `yield return` = lazy, speicherschonend, compiler-generierte Zustandsmaschine. Manueller `IEnumerator` = mehr Kontrollcode, in der Praxis selten nötig.
Diese 3 Anfängerfehler solltest du vermeiden
Die klassischen Stolperfallen rund um Iteratoren sind in Prüfung und Praxis gleich - hier die drei häufigsten.
1. Sammlung während der Iteration verändern
Das gilt für `List<T>` genauso wie für deine eigene Sammlung:
// FALSCH: loest InvalidOperationException aus
List<Song> songs = new List<Song>();
foreach (Song song in songs)
{
if (song.LaengeInSekunden > 340)
{
songs.Remove(song); // Krach!
}
}Warum? Der Iterator kann seinen Zustand nicht garantieren, wenn sich die Datenstruktur unter ihm ändert. Sammle Löschungen erst in einer separaten Liste oder nutze eine `for`-Schleife rückwärts.
2. Current vor MoveNext aufrufen
Ein frischer Enumerator steht vor dem ersten Element. `Current` liefert dort keinen sinnvollen Wert. Erst `MoveNext()` positioniert den Cursor - genau das macht `foreach` für dich.
3. Vergessen, dass yield-Code verzögert läuft
var kurz = FiltereKurzeSongs(playlist); // noch nichts passiert!
foreach (var s in kurz)
{
Console.WriteLine(s.Titel); // JETZT laeuft der Code
}Wenn deine Methode `yield return` enthält, passiert beim Methodenaufruf selbst nichts - erst beim Iterieren. Wer das nicht weiß, wundert sich über "nicht funktionierende" Validierung oder Logging-Code. Dasselbe Prinzip steckt übrigens hinter LINQ, wie wir im Artikel zu LINQ filtern und transformieren zeigen.
Übungstipp: Bau dir einen Wochenplan-Iterator
Nimm eine Klasse `Wochenplan`, die intern ein Array mit sieben `string`-Einträgen hält (Montag bis Sonntag). Implementiere `IEnumerable<string>` mit `yield return` so, dass die Schleife nur die Werktage liefert. Danach erweiterst du sie: eine zweite Methode `Wochenende()` gibt nur Samstag und Sonntag zurück. So internalisierst du sofort, dass Iterator-Logik und Datenstruktur getrennt sind.
Wer lieber Python lernt, findet dasselbe Pattern im Artikel zum Iterator Pattern in Python - ideal, um das Konzept sprachübergreifend zu festigen. Grundlagen zu den Standard-Containern liefert unser C# Collections Überblick.
FAQ: Häufige Fragen zum C# Iterator Pattern
Ist foreach das Iterator Pattern?
`foreach` ist der Verbraucher des Patterns. Das Pattern selbst steckt in `IEnumerable`/`IEnumerator`. Sobald deine Klasse `GetEnumerator()` anbietet, funktioniert `foreach` darauf automatisch.
Was ist der Unterschied zwischen IEnumerable und IEnumerator?
`IEnumerable<T>` ist die durchlaufbare Sammlung und liefert über `GetEnumerator()` einen Iterator. `IEnumerator<T>` ist dieser Iterator mit `MoveNext()` und `Current`. Kurz: Enumerable = Buch, Enumerator = Lesezeichen.
Wann brauche ich einen eigenen Iterator statt einer List?
Wenn du eine spezielle Durchlaufreihenfolge willst, unendliche oder erst bei Bedarf berechnete Folgen lieferst oder deine interne Struktur verbergen musst. Für reines Sammeln reicht eine `List<T>`.
Ist das Iterator Pattern prüfungsrelevant?
Ja - in IHK-AP1-Aufgaben zu Collections und in Uni-Modulen zu Design Patterns taucht es regelmäßig auf. Meist gefragt: die Rollen von `IEnumerable` vs. `IEnumerator` und was `yield return` macht.
Warum gibt es GetEnumerator() zweimal in meinem Code?
Die explizite Implementierung `IEnumerator IEnumerable.GetEnumerator()` stellt Kompatibilität mit altem, nicht-generischem Code sicher. Sie leitet einfach an die generische Variante weiter - ein standardmäßiges Pattern in produktivem C#-Code.
Fazit
Das C# Iterator Pattern steckt in jeder `foreach`-Schleife, die du schreibst. `IEnumerable<T>` macht deine Klasse durchlaufbar, `IEnumerator<T>` ist der Cursor dahinter, und `yield return` erspart dir die gesamte Handarbeit. Wer das verstanden hat, versteht auch, warum LINQ so elegant funktioniert - und warum man Sammlungen während der Iteration nicht anfasst.
Wenn du bei Design Patterns, C#-Grundlagen oder Prüfungsvorbereitung hängst: Bei study-it.education bekommst du persönliche IT-Nachhilfe - abgestimmt auf Azubis, Studenten und Berufseinsteiger. Melde dich einfach für ein kostenloses Probegespräch.
1:1-Hilfe zu diesem Thema?
Brauchst du jemand, der das mit dir durchgeht?
Statt nochmal lesen: 30 min mit einem Senior-Dev.
Kostenloses Erstgespräch buchen →
So implementierst du das Iterator-Pattern in C# mit IEnumerable und IEnumerator – sauberer Code für Azubis, Studenten und Berufseinsteiger.
marcel.schmidtpeter@study-it.education →Mehr Lesestoff
C# JSON mit System.Text.Json lesen & schreiben – einfach erklärt
JSON-Daten in C# lesen und schreiben? Mit System.Text.Json gelingt dir Serialisierung & Deserialisierung – einfach erklärt für alle IT-Einsteiger.
C# xUnit Unit Test: So schreibst du deine ersten Unit Tests
Unit Tests mit xUnit machen deinen C#-Code belastbar. In diesem Tutorial lernst du [Fact], [Theory] und Assert kennen – für Einsteiger erklärt.
C# Datenbank verbinden: ADO.NET & Entity Framework einfach erklärt
So greifst du aus C# auf Datenbanken zu: mit ADO.NET für direktes SQL oder EF Core als modernem ORM – beide Wege mit Beispielen erklärt.