Mise à jour 17/07/2012: Apparemment à partir de C # 5.0, le comportement de foreach
décrit ci-dessous a été modifié et « l'utilisation d'une foreach
variable d'itération dans une expression lambda imbriquée ne produit plus de résultats inattendus. » Cette réponse ne s'applique pas à C # ≥ 5.0 .
@John Skeet et tous ceux qui préfèrent le mot-clé foreach.
Le problème avec "foreach" en C # avant 5.0 , c'est qu'il est incompatible avec le fonctionnement de l'équivalent "pour la compréhension" dans d'autres langues, et avec la façon dont je m'attendrais à ce qu'il fonctionne (opinion personnelle indiquée ici uniquement parce que d'autres ont mentionné leur opinion sur la lisibilité). Voir toutes les questions concernant " Accès à la fermeture modifiée " ainsi que " Fermeture sur la variable de boucle considérée comme nuisible ". Ceci n'est "nuisible" qu'en raison de la façon dont "foreach" est implémenté en C #.
Prenez les exemples suivants en utilisant la méthode d'extension fonctionnellement équivalente à celle de la réponse de @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);
}
}
}
Toutes mes excuses pour l'exemple trop artificiel. J'utilise uniquement Observable parce que ce n'est pas totalement exagéré de faire quelque chose comme ça. Évidemment, il existe de meilleures façons de créer cet observable, j'essaie seulement de démontrer un point. En règle générale, le code souscrit à l'observable est exécuté de manière asynchrone et potentiellement dans un autre thread. Si vous utilisez "foreach", cela pourrait produire des résultats très étranges et potentiellement non déterministes.
Le test suivant utilisant la méthode d'extension "ForEach" réussit:
[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()));
}
Ce qui suit échoue avec l'erreur:
Attendu: équivalent à <0, 1, 2, 3, 4, 5, 6, 7, 8, 9> Mais était: <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()
.