Update 17.07.2012: Anscheinend wurde ab C # 5.0 das foreach
unten beschriebene Verhalten geändert und " die Verwendung einer foreach
Iterationsvariablen in einem verschachtelten Lambda-Ausdruck führt nicht mehr zu unerwarteten Ergebnissen. " Diese Antwort gilt nicht für C # ≥ 5.0 .
@ John Skeet und alle, die das Schlüsselwort foreach bevorzugen.
Das Problem mit "foreach" in C # vor 5.0 ist, dass es nicht mit der Funktionsweise des Äquivalents "für das Verständnis" in anderen Sprachen übereinstimmt und mit der Art und Weise, wie ich es erwarten würde (persönliche Meinung hier nur, weil andere ihre erwähnt haben Meinung zur Lesbarkeit). Siehe alle Fragen zu " Zugriff auf modifizierten Abschluss " sowie " Schließen über die als schädlich geltende Schleifenvariable ". Dies ist nur "schädlich", da "foreach" in C # implementiert ist.
Nehmen Sie die folgenden Beispiele mit der funktional äquivalenten Erweiterungsmethode, die der Antwort von @Fredrik Kalseth entspricht.
public static class Enumerables
{
public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
{
foreach (T item in @this)
{
action(item);
}
}
}
Entschuldigung für das übermäßig erfundene Beispiel. Ich benutze Observable nur, weil es nicht weit hergeholt ist, so etwas zu tun. Offensichtlich gibt es bessere Möglichkeiten, dieses Beobachtbare zu schaffen. Ich versuche nur, einen Punkt zu demonstrieren. Normalerweise wird der Code, der das Observable abonniert hat, asynchron und möglicherweise in einem anderen Thread ausgeführt. Bei Verwendung von "foreach" kann dies zu sehr seltsamen und möglicherweise nicht deterministischen Ergebnissen führen.
Der folgende Test mit der Erweiterungsmethode "ForEach" besteht:
[Test]
public void ForEachExtensionWin()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
values.ForEach(value =>
source.OnNext(() => value));
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Win
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
Folgendes schlägt mit dem Fehler fehl:
Erwartet: entspricht <0, 1, 2, 3, 4, 5, 6, 7, 8, 9>, war aber: <9, 9, 9, 9, 9, 9, 9, 9, 9, 9>
[Test]
public void ForEachKeywordFail()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
foreach (var value in values)
{
//If you have resharper, notice the warning
source.OnNext(() => value);
}
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Fail
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
ForEach()
.