C# -Die Auflistung wurde geändert – Der Enumerationsvorgang kann möglicherweise nicht ausgeführt werden

Eine Meldung ähnlich wie die Überschrift kann man ganz leicht erhalten, wenn man mit Auflistungen in einer Anwendung arbeitet, welche mehrere Threads verwendet und ein Thread lesend und ein anderer schreiben auf die Auflistung zugreift.

Nun ist es so, dass es zu diesem Thema eine ganze Reihe, von mehr oder weniger, guten Ansätzen im Internet zu finden ist (Natürlich überwiegend gute 🙂 ). Wobei eine der entscheidenden Aussagen ist, dass man diese Fehlermeldungen, dann erhält wenn man mit einer Enumeration (foreach Schleife) über die Auflistung geht und dabei neue Elemente der Auflistung hinzufügen oder Elemente löschen möchte.

Also zum Beispiel wie in der folgenden Klasse:

public class AppointmentList : List<Appointment>
{
	public void DeleteAppointmentsForDate(DateTime dt)
	{
		foreach (Appointment appointment in this)
		{
			if (appointment.StartDate.Date == dt.Date)
			{
				Remove(appointment);
			}
		}
	}
}

Eigentlich soll, die Methode der Klasse alle Appointments eines bestimmten Datums aus der Auflistung löschen.
Das mach sie auch, und zwar dann, wenn alles in einem Thread abläuft.

Aber nur dann.

Nun wird bei den meisten Lösungen im Internet vorgeschlagen anstelle der Iteration mit foreach einfach eine for Schleife zu verwenden.

Das würde dann so aussehen
(Ich möchte aber gleich hier sagen, dass diese Lösung für das Löschen auch nicht eingesetzt werden kann, da diese egal ob mit oder ohne mehreren Threads nicht richtig arbeitet).

public class AppointmentList : List<Appointment>
{
		for (int i = 0; i < this.Count; i++)
		{
			if (this[i].StartDate.Date == dt.Date)
			{
				this.RemoveAt(i);
			}
		}
	}
}

Und warum geht das nicht ?

Nehmen wir der Einfachheit halber mal an die Auflistung würde 4 Einträge enthalten und alle 4 würden das gleiche Startdatum haben, was bedeuten würde, dass alle 4 Listen Elemente gelöscht werden müssten.

Runde 1:
i == 0 und this.count== 3
Element an Index 0 wird gelöscht
Element mit Index 1 wird nun an Index Position 0 geführt
Element mit Index 2 wird nun an Index Position 1 geführt
Element mit Index 3 wird nun an Index Position 2 geführt
this.count ist nun nur noch 2

Runde 2:
i == 1 und this.count == 2
….

Eigentlich brauche ich gar nicht mehr weiter erläutern, jetzt ist klar, dass das löschen mit der for schleife mit erhöhendem Index (i++) nicht funktionieren kann, wenigstens nicht wenn es um das Löschen von List Elementen geht.

Aber natürlich gibt es auch dafür eine einfache Lösung, und diese sieht so aus:

public class AppointmentList : List<Appointment>
{
	public void DeleteAppointmentsForDate(DateTime dt)
	{
            for (int i = this.Count - 1; i >= 0; i--)
            {
                if (this[i].StartDate.Date == dt.Date)
                {
                    this.RemoveAt(i);
                }
            }
		}
	}
}

Genau, wir verwenden einfach eine for schleife mit einem herabzählendem Index (i–).
Dies funktioniert auch mit mehreren Threads.