CodeGym /Kurse /C# SELF /Element aus einer Collection im

Element aus einer Collection im foreach-Loop löschen

C# SELF
Level 29 , Lektion 4
Verfügbar

1. Einführung

Fast jeder, der mit C# anfängt zu programmieren, stößt früher oder später auf das gleiche Problem: Es gibt eine Collection (zum Beispiel eine Liste von Objekten) und man will unerwünschte Elemente nach einer bestimmten Bedingung entfernen. Klingt easy, und die Hand greift direkt zum bekannten, bequemen foreach-Loop, weil das ja der “sicherste” und “freundlichste” Weg zum Durchlaufen ist. Doch plötzlich, im unerwartetsten Moment, taucht ein mysteriöser Laufzeitfehler auf, den es bei einfachen Beispielen nicht gab – und das Programm bleibt einfach stehen.

Lass uns anschauen, warum das passiert, was “unter der Haube” bei Collections und Iteratoren abgeht und wie man Elemente richtig entfernt, damit es keine bösen Überraschungen und Bugs gibt.

Warum foreach nicht mit Löschen klarkommt

Um zu fühlen, was da passiert, stell dir eine Warteschlange von Leuten vor (das ist unsere Collection). Du gehst die Schlange entlang und fragst jeden: "Soll ich dich behalten oder streichen?" Wenn du anfängst, jemanden direkt beim Durchgehen zu streichen, verschiebt sich die ganze Schlange, Leute bewegen sich, und dein Plan “nächste Person = nächste in der Liste” geht sofort schief. Vielleicht wird jemand übersprungen oder doppelt gefragt.

Beispiel in C#:


List<string> names = new List<string> { "Anton", "Boris", "Vika", "Grisha" };

foreach (string name in names)
{
    if (name.StartsWith("V"))
        names.Remove(name); // Boom! InvalidOperationException
}

Wenn das Programm bei "Vika" ankommt und sie löschen will, verliert der interne Iterator den “Kontakt zur Realität” – und du bekommst die Meldung:
InvalidOperationException: Collection was modified; enumeration operation may not execute.

Das ist kein Scherz – so schützt dich C# vor schwer auffindbaren Bugs und kaputten Datenstrukturen.

2. Warum funktioniert so ein simpler Code nicht?

Wie läuft das intern ab?

Wenn du einen foreach-Loop schreibst, erzeugt der Compiler ein spezielles Objekt – einen Iterator (IEnumerator), der die aktuelle Position in der Collection verfolgt. Dieses Objekt merkt sich, wie viele Elemente es am Anfang gab, welches Element gerade “aktiv” ist, und achtet streng darauf, dass die Collection während des Durchlaufs nicht verändert wird.

Jeder Versuch, ein Element im foreach zu löschen oder hinzuzufügen, bricht diesen Vertrag. Warum? Wenn nach deinem Löschen die Indizes verrutschen, kann der Iterator nicht mehr korrekt zum nächsten Element springen. Manche werden übersprungen, andere doppelt gezählt – am Ende gibt’s totales Chaos. Deshalb wirft .NET schon beim ersten Ändern der Collection ehrlich und klar einen Fehler.

Was passiert beim “direkten” Löschen?

Stell dir vor, du schreibst so ein Programm:


List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
foreach (int x in numbers)
{
    if (x % 2 == 0)
        numbers.Remove(x);
}

Sieht logisch aus: alle Zahlen durchgehen, gerade löschen. Aber beim zweiten Durchlauf gibt’s einen Fehler – “Collection wurde während der Enumeration geändert”.

Manchmal will man diese Warnung umgehen und es “auf eigene Gefahr” probieren. Aber selbst wenn es keinen Fehler gäbe, wäre das Ergebnis je nach Collection-Struktur unvorhersehbar. Du könntest versehentlich über Elemente “springen” oder nicht alle löschen, die du wolltest.

3. Wie macht man’s richtig?

Technik #1: Rückwärts-Loop mit for

Das Problem ist, dass beim Löschen ein Element die nachfolgenden nach links verschiebt, und wenn du von vorne durchgehst, kannst du leicht Indizes verwechseln und Elemente überspringen. Um das zu vermeiden, gehst du einfach von hinten los.


List<string> names = new List<string> { "Anton", "Boris", "Vika", "Grisha" };

for (int i = names.Count - 1; i >= 0; i--)
{
    if (names[i].StartsWith("V"))
        names.RemoveAt(i);
}

In diesem Beispiel werden nach jedem Löschen alle nachfolgenden Elemente verschoben, aber die Indizes, die wir noch nicht bearbeitet haben, bleiben unberührt. So wird nichts übersprungen.

Technik #2: Filtern und neue Liste bauen

Manchmal ist es einfacher (und oft schneller), die Collection durchzugehen, nur die Elemente zu sammeln, die bleiben sollen, und die ursprüngliche Liste durch die neue zu ersetzen.


var names = new List<string> { "Anton", "Boris", "Vika", "Grisha" };
names = names.Where(name => !name.StartsWith("V")).ToList();
// Am Ende bleiben "Anton" und "Grisha"

Das ist praktisch, wenn die Collection nicht zu groß ist oder es nicht kritisch ist, die ursprüngliche Referenz zu behalten.

Technik #3: Spezielle Methoden der Collection nutzen

Wenn du mit einem klassischen List<T> arbeitest, gibt’s für das Löschen nach Bedingung eine praktische Methode:


names.RemoveAll(name => name.StartsWith("V"));

Das läuft intern korrekt ab und du bekommst kompakten, verständlichen Code.

Technik #4: Zum Löschen sammeln

Es gibt Collections, die man nicht “on the fly” ändern kann (zum Beispiel Dictionary, HashSet oder sogar deine eigene Klasse). In solchen Fällen nutzt man die “zum Löschen markieren”-Methode:

  1. Erst die Collection durchgehen und alle zu löschenden Elemente in eine extra Liste packen.
  2. Dann diese neue Liste durchgehen und die Elemente aus der Original-Collection löschen.

Dictionary<int, string> dict = new Dictionary<int, string> { [1] = "eins", [2] = "zwei", [3] = "drei" };
var toDelete = new List<int>();
foreach (var kvp in dict)
{
    if (kvp.Key % 2 == 0)
        toDelete.Add(kvp.Key);
}
foreach (var key in toDelete)
    dict.Remove(key);

4. Nützliche Details

Fehler und Mythen von Anfängern

Einer der häufigsten Fehler ist zu erwarten, dass das Löschen eines Elements aus einer Collection während des Durchlaufs schon “irgendwie” klappt, weil das in manchen anderen Sprachen (zum Beispiel Python) oft möglich ist. Aber in C# ist das strikt verboten – zu deinem eigenen Schutz: Lieber ein klarer Fehler als ein leiser, fieser Bug, den später niemand mehr findet.

Ein weiterer häufiger Fehler: Einen for-Loop mit steigendem Index zu benutzen statt mit abnehmendem. Dadurch werden nach dem Löschen alle folgenden “verschoben” und manche Elemente werden übersprungen. Geh immer von hinten nach vorne, wenn du nach Index löschst.

Die Moral von der Geschichte

Die Aufgabe “Elemente aus einer Collection nach Bedingung löschen” kommt in jedem zweiten C#-Programm vor, aber direkt im foreach-Loop darfst du das nicht machen – das ist die Architektur der Sprache, damit deine Daten konsistent bleiben und keine unerwarteten Fehler passieren.
Merk dir diese Regel, dann sparst du dir viele schlaflose Nächte mit dem Debugger.

So machst du es immer richtig

  • Lösch niemals Elemente einer Collection direkt im foreach-Loop. Das führt zu einem Laufzeitfehler.
  • Für Listen (List<T>) und Arrays nutze entweder einen for-Loop von hinten oder die Methoden RemoveAll und Filterung via LINQ.
  • Für Dictionaries, Sets und andere komplexe Collections – erst die zu löschenden Elemente sammeln, dann durch diese Liste gehen und aus der Original-Collection löschen.
  • Wenn du unsicher bist – überleg: Wie verändert sich die Collection beim Löschen? Was macht der Iterator? Wenn du auch nur ein bisschen zweifelst, ist die Methode wahrscheinlich falsch.
1
Umfrage/Quiz
Filtern von Elementen, Level 29, Lektion 4
Nicht verfügbar
Filtern von Elementen
Arbeiten mit Kollektionen
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION