J'aimerais faire l'équivalent de ce qui suit en LINQ, mais je n'arrive pas à comprendre comment :
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
Quelle est la véritable syntaxe ?
J'aimerais faire l'équivalent de ce qui suit en LINQ, mais je n'arrive pas à comprendre comment :
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
Quelle est la véritable syntaxe ?
Il n'y a pas d'extension ForEach pour IEnumerable
; uniquement pour les List<T>
. Vous pouvez donc faire
items.ToList().ForEach(i => i.DoStuff());
Vous pouvez également écrire votre propre méthode d'extension ForEach :
public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach(T item in enumeration)
{
action(item);
}
}
J'avais une méthode de ce type dans mon code, mais en réalité ToList().ForEach() résout le problème et je pense que la réponse de Jon, ci-dessous, est plus correcte.
Soyez prudent avec ToList(), car ToList() crée une copie de la séquence, ce qui peut entraîner des problèmes de performance et de mémoire.
Il y a quelques raisons pour lesquelles ".Foreach()" est préférable. 1. Multi-threading, possible avec PLINQ. 2. Exceptions, vous pouvez vouloir collecter toutes les exceptions dans la boucle et les lancer en une seule fois ; et ce sont des codes de bruit.
Fredrik a fourni le correctif, mais il peut être utile de se demander pourquoi cela ne figure pas dans le cadre dès le départ. Je pense que l'idée est que les opérateurs de requête LINQ devraient être sans effet de bord, s'intégrant dans une manière raisonnablement fonctionnelle de voir le monde. Il est clair que ForEach est exactement le contraire - un purement une construction basée sur les effets secondaires.
Cela ne veut pas dire qu'il s'agit d'une mauvaise chose à faire - il s'agit simplement de réfléchir aux raisons philosophiques qui sous-tendent cette décision.
À titre de référence, cette question pose la même problématique et reçoit l'avis des "pouvoirs en place" : stackoverflow.com/questions/858978/
Cependant, il y a une chose très utile que les fonctions LINQ fournissent typiquement et que foreach () ne fournit pas - la fonction anonyme prise par les fonctions Linq peut aussi prendre un index comme paramètre, alors qu'avec foreach vous devez reconstruire la construction classique for (int i..). Et les langages fonctionnels doivent permettre l'itération qui a des effets de bord - sinon vous ne pourriez jamais faire un iota de travail avec eux :) J'aimerais souligner que la méthode fonctionnelle typique consisterait à renvoyer l'énumérable original inchangé.
@Walt : Si une méthode fonctionnelle typique renvoyait l'énumérable original et le n'a pas ont des effets secondaires (qui typique (les méthodes fonctionnelles ne le font pas), il n'y aurait alors aucun effet. Peut-être pourriez-vous clarifier votre commentaire ?
Mise à jour 7/17/2012 : Apparemment, à partir de C# 5.0, le comportement de foreach
décrite ci-dessous a été modifiée et " l'utilisation d'un foreach
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 la version 5.0 L'opinion personnelle exprimée ici, uniquement parce que d'autres ont mentionné leur opinion concernant la lisibilité, est qu'elle n'est pas cohérente avec la manière dont l'équivalent "pour la compréhension" fonctionne dans d'autres langues, et avec la manière dont je m'attendrais à ce qu'il fonctionne. Voir toutes les questions concernant " Accès à une fermeture modifiée " ainsi que " La fermeture de la variable de la boucle est considérée comme nuisible ". Cette situation n'est "préjudiciable" qu'en raison de la manière dont "foreach" est mis en œuvre 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);
}
}
}
Je m'excuse pour cet exemple trop artificiel. Je n'utilise Observable que parce que ce n'est pas complètement farfelu de faire quelque chose comme ça. Il est évident qu'il y a de meilleures façons de créer cet observable, j'essaie seulement de démontrer un point. Typiquement, le code souscrit à l'observable est exécuté de manière asynchrone et potentiellement dans un autre thread. Si l'on utilise "foreach", cela pourrait produire des résultats très étranges et potentiellement non déterministes.
Le test suivant, qui utilise la méthode d'extension "ForEach", est réussi :
[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()));
}
L'opération suivante échoue avec l'erreur :
Attendu : équivalent à < 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 > Mais c'était le cas : < 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()));
}
@reggaeguitar Je n'ai pas utilisé C# depuis 7 ou 8 ans, depuis avant la sortie de C# 5.0, qui a changé le comportement décrit ici. A l'époque, il donnait cet avertissement stackoverflow.com/questions/1688465/ . Je doute qu'il soit encore en alerte sur ce point.
Vous pouvez utiliser le FirstOrDefault()
qui est disponible pour les IEnumerable<T>
. En renvoyant false
du prédicat, il sera exécuté pour chaque élément mais ne se souciera pas du fait qu'il ne trouve pas de correspondance. Cela permet d'éviter le problème de la ToList()
de la tête.
IEnumerable<Item> items = GetItems();
items.FirstOrDefault(i => { i.DoStuff(); return false; });
Je suis également d'accord. Il s'agit peut-être d'une petite astuce, mais à première vue, ce code n'est pas très clair, en tout cas par rapport à une boucle foreach standard.
J'ai dû rétrograder parce que, bien que techniquement correct, votre futur moi et tous vos successeurs vous détesteront à jamais parce qu'au lieu de foreach(Item i in GetItems()) { i.DoStuff();}
Vous avez pris plus de personnages et vous avez rendu les choses extrêmement confuses.
J'ai repris la méthode de Fredrik et j'ai modifié le type de retour.
De cette manière, la méthode prend en charge exécution différée comme les autres méthodes LINQ.
EDITAR: Si cela n'était pas clair, toute utilisation de cette méthode doit se terminer par ToList() ou tout autre moyen de forcer la méthode à fonctionner sur l'énumérable complet. Dans le cas contraire, l'action ne serait pas exécutée !
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach (T item in enumeration)
{
action(item);
yield return item;
}
}
Et voici le test qui permet de s'en rendre compte :
[Test]
public void TestDefferedExecutionOfIEnumerableForEach()
{
IEnumerable<char> enumerable = new[] {'a', 'b', 'c'};
var sb = new StringBuilder();
enumerable
.ForEach(c => sb.Append("1"))
.ForEach(c => sb.Append("2"))
.ToList();
Assert.That(sb.ToString(), Is.EqualTo("121212"));
}
Si vous retirez le ToList() à la fin, vous verrez que le test échoue car le StringBuilder contient une chaîne vide. C'est parce qu'aucune méthode n'a forcé le ForEach à énumérer.
Votre autre mise en œuvre de ForEach
est intéressante, mais elle ne correspond pas au comportement de List.ForEach
dont la signature est public void ForEach(Action<T> action)
. Elle ne correspond pas non plus au comportement de la Observable.ForEach
extension à IObservable<T>
dont la signature est public static void ForEach<TSource>(this IObservable<TSource> source, Action<TSource> onNext)
. A titre d'information, le ForEach
équivalent aux collections Scala, même celles qui sont paresseuses, ont également un type de retour qui est équivalent à void en C#.
Cela fonctionne exactement de la même manière que l'appel à Select
con ToList
. L'objectif de la ForEach
est de ne pas avoir à appeler ToList
. Il doit être exécuté immédiatement.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
4 votes
@pst : J'avais l'habitude de coller aveuglément ces méthodes d'extension dans mes projets. Merci pour cet article.
3 votes
Il y a PlusLINQ qui dispose d'un
ForEach
extension .0 votes
Voici une autre idée de la façon dont cette pourrait être possible : visualstudio.uservoice.com/forums/121579-visual-studio-2015/
4 votes
Même si ce n'est pas très sexy, cela tient sur une ligne sans créer de copie via ToList -
foreach (var i in items) i.Dostuff();
3 votes
Certains de ces liens sont morts ! Quel est l'article qui a fait l'objet d'un si grand nombre de votes !