20% off with code ERSTIS26
5 min read 20 Aug 2026

IEnumerable und foreach in C# verstehen

Stell dir vor, du sitzt in deiner Ausbildung, im Studium oder im ersten Job vor einer Aufgabe: Du sollst alle Einträge einer Kundendatei ausgeben. Du tippst `foreach (Kunde k in kunden)` - und es funktioniert einfach. Bei Listen, bei Arrays, bei Dictionaries. Fragst du dich irgendwann, *warum* das überall klappt, landest du unweigerlich bei IEnumerable - dem Interface, das hinter fast jeder Schleife in C# steckt. Was ist IEnumerable in C#? IEnumerable ist ein Interface aus dem Namespace...

Marcel Schmidtpeter By Marcel Schmidtpeter 20 Aug 2026
IEnumerable und foreach in C# verstehen

Stell dir vor, du sitzt in deiner Ausbildung, im Studium oder im ersten Job vor einer Aufgabe: Du sollst alle Einträge einer Kundendatei ausgeben. Du tippst `foreach (Kunde k in kunden)` - und es funktioniert einfach. Bei Listen, bei Arrays, bei Dictionaries. Fragst du dich irgendwann, *warum* das überall klappt, landest du unweigerlich bei IEnumerable - dem Interface, das hinter fast jeder Schleife in C# steckt.

Was ist IEnumerable in C#?

IEnumerable ist ein Interface aus dem Namespace `System.Collections.Generic`, das genau eine einzige Methode vorschreibt: `GetEnumerator()`. Wer es implementiert, sagt dem Compiler: „Von mir kannst du Elemente eins nach dem anderen abholen."

Stell dir eine Kasse im Supermarkt vor: Die Kundin an der Kasse (das ist `foreach`) fragt nicht selbst im Regal, sondern der Kassierer (der Enumerator) legt ihr ein Produkt nach dem anderen auf das Band. Die Schnittstelle zwischen beiden ist geregelt - egal ob es sich um Lebensmittel, Werkzeug oder Bücher handelt.

Merke: `IEnumerable<T>` ist der Vertrag, der einer Sammlung sagt: „Du bist durchlaufbar." Die `foreach`-Schleife ist der Code, der diesen Vertrag einlöst.

Die generische Version `IEnumerable<T>` (also z.B. `IEnumerable<string>`) solltest du der alten, nicht-generischen Variante `IEnumerable` immer vorziehen. Warum? Weil du dadurch den Typ der Elemente schon zur Compilezeit kennst und keine Typumwandlungen brauchst.

Wie hängen IEnumerable und foreach zusammen?

`foreach` funktioniert mit jedem Typ, der `IEnumerable` bzw. `IEnumerable<T>` implementiert. Das sind unter anderem `List<T>`, Arrays (`T[]`), `Dictionary<TKey, TValue>` und `HashSet<T>`. Intern übersetzt der Compiler deine Schleife in etwas wie das hier:

csharp
List<string> namen = new List<string> { "Anna", "Ben", "Clara" };

// Das schreibst du:
foreach (string name in namen)
{
  Console.WriteLine(name);
}

// Das macht der Compiler daraus (vereinfacht):
IEnumerator<string> enumerator = namen.GetEnumerator();
while (enumerator.MoveNext())
{
  string name = enumerator.Current;
  Console.WriteLine(name);
}

Der Enumerator (das Objekt, das `GetEnumerator()` zurückgibt) verwaltet zwei zentrale Mitglieder: `MoveNext()` springt zum nächsten Element und liefert `true`, solange es weitergeht. `Current` gibt das aktuelle Element zurück.

Ist die Sammlung leer oder am Ende, liefert `MoveNext()` einfach `false` - und die Schleife endet sauber, ohne dass du selbst einen Zähler verwalten musst.

Kernpunkt: `foreach` braucht nichts weiter als `GetEnumerator()`. Jeder Typ, der das liefert, ist automatisch foreach-fähig.

Wie machst du eine eigene Klasse foreach-fähig?

Mit `yield return` geht das erstaunlich einfach - und zwar ohne, dass du je selbst einen Enumerator baust:

csharp
using System;
using System.Collections;
using System.Collections.Generic;

public class Wochenplan : IEnumerable<string>
{
  private readonly string[] tage = { "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag" };

  public IEnumerator<string> GetEnumerator()
  {
    for (int i = 0; i < tage.Length; i++)
    {
      yield return tage[i];
    }
  }

  // Die nicht-generische Variante wird fuer die Kompatibilitaet benoetigt
  IEnumerator IEnumerable.GetEnumerator()
  {
    return GetEnumerator();
  }
}

class Program
{
  static void Main()
  {
    Wochenplan plan = new Wochenplan();

    foreach (string tag in plan)
    {
      Console.WriteLine(tag);
    }
  }
}

`yield return` erzeugt den Enumerator automatisch: Der Compiler baut daraus eine Klasse, die sich merkt, wo die Iteration steht. Du schreibst einfach, *welche* Werte in welcher Reihenfolge geliefert werden sollen - den Rest übernimmt C#. Das ist der gleiche Mechanismus, den auch LINQ unter der Haube nutzt. Details dazu findest du in der offiziellen yield-Dokumentation von Microsoft Learn.

Ein praktischer Nebeneffekt: Du kannst so auch unendliche Folgen erzeugen (z.B. eine Methode, die endlos Zufallszahlen liefert), weil Werte erst beim Abruf berechnet werden. Das nennt man Deferred Execution - verzögerte Ausführung. Vorsicht allerdings: Konsumierst du so etwas mit einer klassischen `while`-Schleife ohne Abbruchbedingung, läuft dein Programm ewig weiter.

IEnumerable<T> oder List<T> - was nehme ich?

Die kurze Antwort: Nimm `IEnumerable<T>` als Parameter- und Rückgabetyp, wenn du nur durchlaufen willst. Nimm `List<T>`, wenn du Elemente hinzufügen, löschen oder per Index zugreifen musst.

Diese Frage stellt sich spätestens, wenn du Methodensignaturen entwirfst:

Eigenschaft

`IEnumerable<T>`

`List<T>`

Durchlaufen mit `foreach`

Ja

Ja

Zugriff per Index (`liste[0]`)

Nein

Ja

Elemente hinzufügen/entfernen

Nein (direkt)

Ja

Ausführung bei LINQ

oft verzögert

sofort (materialisiert)

Geeignet als Methodenparameter

sehr flexibel

nur wenn Änderungen nötig sind

Merke: `IEnumerable<T>` ist der flexibelste Typ - `List<T>`, Arrays und LINQ-Ergebnisse passen alle rein. Wer `List<T>` als Parameter verlangt, schreckt Aufrufer unnötig ab.

Wer sich generell mit Schnittstellen und Verträgen in der Objektorientierung schwer tut, sollte sich zuerst die OOP-Grundlagen in C# anschauen - IEnumerable ist nämlich genau genommen nichts anderes als ein solcher Vertrag.

Welche Fehler passieren bei IEnumerable und foreach häufig?

Drei Stolperfallen treffen Anfänger wie Fortgeschrittene - und alle drei sind beliebte Prüfungsfragen.

Fehler 1: Sammlung während der Schleife verändern

Der Klassiker - und eine beliebte Prüfungsfrage in der IHK-Abschlussprüfung:

csharp
List<string> namen = new List<string> { "Anna", "Ben", "Clara" };

// Falsch: wirft eine InvalidOperationException
foreach (string name in namen)
{
  if (name == "Ben")
  {
    namen.Remove(name); // Veraendern waehrend der Iteration ist verboten
  }
}

// Richtig: erst sammeln, dann entfernen
List<string> zuEntfernen = new List<string>();
foreach (string name in namen)
{
  if (name == "Ben")
  {
    zuEntfernen.Add(name);
  }
}

foreach (string name in zuEntfernen)
{
  namen.Remove(name);
}

Warum der Fehler? Der Enumerator erwartet die Sammlung in einem konsistenten Zustand. Änderst du während des Durchlaufs die Datenstruktur, stimmen die internen Zähler nicht mehr - C# reagiert mit einer `InvalidOperationException`. Kompakter löst du das Ganze übrigens mit `namen.RemoveAll(n => n == "Ben");`.

Bei Arrays tritt dieser Fehler übrigens nicht auf, weil sie eine feste Größe haben - Elemente entfernen kannst du dort ohnehin nicht.

Fehler 2: Mehrfache Enumeration

Wenn du ein `IEnumerable<T>` (z.B. ein LINQ-Ergebnis) zweimal mit `foreach` durchläufst, wird die Abfrage unter Umständen zweimal vollständig ausgeführt - inklusive Filterung oder Datenbankzugriff:

csharp
using System.Linq;

IEnumerable<string> ergebnis = namen.Where(n => n.StartsWith("A"));

Console.WriteLine(ergebnis.Count()); // 1. Durchlauf: Abfrage laeuft komplett
foreach (string name in ergebnis)    // 2. Durchlauf: Abfrage laeuft erneut
{
  Console.WriteLine(name);
}

Lösung: einmal materialisieren mit `ToList()` oder `ToArray()`, wenn du das Ergebnis mehrfach brauchst.

Fehler 3: Die foreach-Variable für Zuweisungen missbrauchen

Die Schleifenvariable ist schreibgeschützt. `name = "Test";` innerhalb der Schleife kompiliert nicht. Die Variable ist nur eine Kopie des aktuellen Elements, keine Referenz auf seinen Platz in der Sammlung.

Bei Referenztypen kannst du zwar das Objekt selbst verändern (z.B. `kunde.Name = "Neu";`), aber nicht die Variable auf ein anderes Objekt zeigen lassen. Bei Wertetypen wie `int` geht beides nicht.

Übungstipp: Bau dir eine eigene Zahlenfolge

Nimm dir 20 Minuten und implementiere eine Klasse `GeradeZahlen : IEnumerable<int>`, die mit `yield return` alle geraden Zahlen von 2 bis 100 liefert. Durchlaufe sie danach mit `foreach` und gib nur die Zahlen aus, die durch 4 teilbar sind. Diese Übung kombiniert Interfaces, `yield` und Schleifenlogik - genau das Wissen, das in Prüfungen und Vorstellungsgesprächen abgefragt wird. Die API-Referenz zu `IEnumerable<T>` auf Microsoft Learn hilft dir dabei weiter.

FAQ: Häufige Fragen zu IEnumerable und foreach

Was ist der Unterschied zwischen IEnumerable und IEnumerator?

`IEnumerable` ist die Sammlung selbst - sie hat eine Methode `GetEnumerator()`. `IEnumerator` ist das Werkzeug, das beim Durchlauf die aktuelle Position und das aktuelle Element verwaltet (`MoveNext()`, `Current`). Die `foreach`-Schleife bedient beides automatisch.

Funktioniert foreach auch mit Arrays?

Ja. Arrays implementieren `IEnumerable<T>` (und `IEnumerable`) automatisch, deshalb kannst du sie direkt mit `foreach` durchlaufen - ganz ohne Umweg über eine Liste.

Was macht yield return genau?

Es liefert ein Element an den Aufrufer zurück, ohne die Methode zu beenden. Beim nächsten Durchlauf wird genau dort weitergemacht. So schreibst du Iteratoren, ohne selbst eine Enumerator-Klasse zu bauen.

Wann sollte eine Methode IEnumerable zurückgeben?

Immer dann, wenn die Aufrufer das Ergebnis nur lesen und durchlaufen sollen. Das gilt allerdings nicht, wenn der Aufrufer die Sammlung verändern oder per Index zugreifen muss - dann ist `List<T>` oder `IList<T>` die bessere Wahl.

Warum bekomme ich eine InvalidOperationException in meiner foreach-Schleife?

Höchstwahrscheinlich veränderst du die Sammlung während der Iteration (z.B. mit `Remove` oder `Add`). Sammle Änderungen stattdessen und führe sie nach der Schleife aus.

Fazit

`IEnumerable` und `foreach` sind ein perfekt aufeinander abgestimmtes Duo: Das Interface definiert den Vertrag „durchlaufbar", die Schleife löst ihn ein - und `yield return` lässt dich eigene durchlaufbare Klassen schreiben, ohne eine Enumerator-Klasse von Hand zu bauen. Wenn du das Prinzip einmal verinnerlicht hast, verstehst du auch, wie LINQ unter der Haube funktioniert. Weiterführend lohnt sich ein Blick auf Anonyme Typen und Lambdas in C#, weil LINQ-Abfragen beides kombinieren.

Wenn du bei C# oder anderen IT-Themen aus Ausbildung, Studium oder Berufseinstieg festhängst, hilft dir die Einzelnachhilfe bei study-it.education weiter - individuell, praxisnah und auf deinem Niveau. Schau vorbei und vereinbare dein kostenloses Probegespräch!


Matching tutoring

Want 1:1 help with this topic?

First chat · €0 · 30 min

Need someone to walk through this with you?

Instead of re-reading: 30 min with a senior dev.

Book a free first chat →
About the author
Marcel Schmidtpeter
Marcel Schmidtpeter

Stell dir vor, du sitzt in deiner Ausbildung, im Studium oder im ersten Job vor einer Aufgabe: Du sollst alle Einträge einer Kundendatei ausgeben. Du tippst `foreach (Kunde k in kunden)` - und es funktioniert einfach. Bei Listen, bei Arrays, bei Dictionaries. Fragst du dich irgendwann, *warum* das überall klappt, landest du unweigerlich bei IEnumerable - dem Interface, das hinter fast jeder Schleife in C# steckt. Was ist IEnumerable in C#? IEnumerable ist ein Interface aus dem Namespace...

marcel.schmidtpeter@study-it.education →
Keep reading

More reading