Ich kenne zwei obskure Dinge, die sie unterscheiden. Los geht's!
Erstens gibt es den klassischen Fehler, für jedes Element in der Liste einen Delegaten zu erstellen. Wenn Sie das foreach-Schlüsselwort verwenden, können alle Ihre Delegaten am Ende auf das letzte Element der Liste verweisen:
// Eine Liste von Aktionen, die später ausgeführt werden sollen
List actions = new List();
// Zahlen von 0 bis 9
List numbers = Enumerable.Range(0, 10).ToList();
// Speichern einer Aktion, die jede Zahl druckt (FALSCH!)
foreach (int number in numbers)
actions.Add(() => Console.WriteLine(number));
// Führen Sie die Aktionen aus, wir drucken tatsächlich 10 Kopien von "9"
foreach (Action action in actions)
action();
// Also versuchen wir es erneut
actions.Clear();
// Speichern einer Aktion, die jede Zahl druckt (RICHTIG!)
numbers.ForEach(number =>
actions.Add(() => Console.WriteLine(number)));
// Führen Sie die Aktionen aus
foreach (Action action in actions)
action();
Die List.ForEach-Methode hat dieses Problem nicht. Das aktuelle Element der Iteration wird an die äußere Lambda als Argument übergeben, und dann erfasst die innere Lambda dieses Argument korrekt in ihrem eigenen Closure. Problem gelöst.
(Leider glaube ich, dass ForEach ein Element von List ist, anstatt eine Erweiterungsmethode zu sein, obwohl es einfach ist, sie selbst zu definieren, damit Sie über diesen Mechanismus auf jeden Enumerable-Typ zugreifen können.)
Zweitens hat der Ansatz der ForEach-Methode eine Einschränkung. Wenn Sie IEnumerable implementieren, indem Sie yield return verwenden, können Sie kein yield return innerhalb der Lambda durchführen. Das Durchlaufen der Elemente in einer Sammlung, um Dinge mittels yield return zurückzugeben, ist auf diese Weise nicht möglich. Sie müssen das foreach-Schlüsselwort verwenden und das Closure-Problem umgehen, indem Sie manuell eine Kopie des aktuellen Schleifenwerts innerhalb der Schleife erstellen.
Hier mehr