Aggiornamento del 17/07/2012: apparentemente a partire da C # 5.0, il comportamento foreach
descritto di seguito è stato modificato e " l'uso di una foreach
variabile di iterazione in un'espressione lambda nidificata non produce più risultati imprevisti. " Questa risposta non si applica a C # ≥ 5.0 .
@ John Skeet e tutti coloro che preferiscono la parola chiave foreach.
Il problema con "foreach" in C # prima della 5.0 è che non è coerente con il modo in cui l'equivalente "per comprensione" funziona in altre lingue e con come mi aspetto che funzioni (opinione personale dichiarata qui solo perché altri hanno menzionato il loro opinione sulla leggibilità). Consulta tutte le domande relative a " Accesso alla chiusura modificata " e " Chiusura sulla variabile loop considerata dannosa ". Questo è solo "dannoso" a causa del modo in cui "foreach" è implementato in C #.
Prendi i seguenti esempi usando il metodo di estensione funzionalmente equivalente a quello nella risposta di @Fredrik Kalseth.
public static class Enumerables
{
public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
{
foreach (T item in @this)
{
action(item);
}
}
}
Ci scusiamo per l'esempio eccessivamente inventato. Sto solo usando Observable perché non è del tutto inverosimile fare qualcosa del genere. Ovviamente ci sono modi migliori per creare questo osservabile, sto solo cercando di dimostrare un punto. In genere il codice sottoscritto all'osservabile viene eseguito in modo asincrono e potenzialmente in un altro thread. Se si utilizza "foreach", questo potrebbe produrre risultati molto strani e potenzialmente non deterministici.
Il seguente test che utilizza il metodo di estensione "ForEach" supera:
[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()));
}
Quanto segue non riesce con l'errore:
Previsto: equivalente a <0, 1, 2, 3, 4, 5, 6, 7, 8, 9> Ma era: <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()
.