Équivalent LINQ de foreach pour IEnumerable <T>

716

Je voudrais faire l'équivalent de ce qui suit dans LINQ, mais je ne peux pas comprendre comment:

IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());

Quelle est la vraie syntaxe?

tags2k
la source
99
Considérez «foreach» vs «ForEach» par Eric Lippert. Il fournit une bonne justification pour ne pas utiliser / fournir a ForEach().
4
@pst: J'avais l'habitude de coller aveuglément cette méthode d'extension dans mes projets. Merci pour cet article.
Robin Maben
2
Il y a MoreLINQ qui a une ForEachextension .
Uwe Keim
2
Bien que pas très sexy, cela tient sur une seule ligne sans créer de copie via ToList -foreach (var i in items) i.Dostuff();
James Westgate
1
Certains de ces liens sont morts! Quel est l'article qui a été autant voté!
Warren

Réponses:

863

Il n'y a pas d'extension ForEach pour IEnumerable; seulement pour List<T>. Donc tu pourrais faire

items.ToList().ForEach(i => i.DoStuff());

Sinon, écrivez 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);
    }
}
Fredrik Kalseth
la source
213
Soyez prudent avec ToList (), car ToList () crée une copie de la séquence, ce qui pourrait entraîner des problèmes de performances et de mémoire.
decasteljau
15
Il y a peu de raisons pour lesquelles ".Foreach ()" sera meilleur. 1. Multi-threading, possible par PLINQ. 2. Exceptions, vous voudrez peut-être collecter toutes les exceptions dans la boucle et les lancer en même temps; et ce sont des codes de bruit.
Dennis C
12
PLINQ utilise ForAll et non ForEach, c'est pourquoi vous n'utiliseriez pas ForEach.
user7116
9
Vous devez renvoyer un IENumerable<T>dans la méthode d'extension. Comme:public static IEnumerable<T> ForAll<T>(this IEnumerable<T> numeration, Action<T> action) { foreach (T item in enumeration) { action(item); } return enumeration; }
Kees C. Bakker
22
Notez que dans le premier cas, le développeur 1) enregistre presque pas de saisie et 2) alloue inutilement de la mémoire pour stocker la séquence entière sous forme de liste avant de la jeter. Réfléchissez avant vous LINQ
Mark Sowul
364

Fredrik a fourni le correctif, mais il vaut peut-être la peine de se demander pourquoi ce n'est pas dans le cadre pour commencer. Je crois que l'idée est que les opérateurs de requête LINQ devraient être sans effet secondaire, s'inscrivant dans une manière raisonnablement fonctionnelle de regarder le monde. Clairement, ForEach est exactement le contraire - une construction purement basée sur les effets secondaires.

Cela ne veut pas dire que c'est une mauvaise chose à faire - il suffit de penser aux raisons philosophiques derrière la décision.

Jon Skeet
la source
44
Et pourtant, F # a Seq.iter
Stephen Swensen
6
Cependant, il y a une chose très utile que les fonctions LINQ fournissent généralement que foreach () ne fait pas - la fonction anonyme prise par les fonctions Linq peut également prendre un index comme paramètre, tandis qu'avec foreach vous devez reconstruire le classique pour (int i .. ) construire. Et les langages fonctionnels doivent permettre une itération qui a des effets secondaires - sinon vous ne pourriez jamais faire un iota de travail avec eux :) Je voudrais souligner que la méthode fonctionnelle typique serait de renvoyer l'énumérable original inchangé.
Walt W
3
@Walt: Je ne suis toujours pas sûr que je suis d' accord, en partie parce qu'une approche véritablement fonctionnelle typique ne serait pas utiliser foreach comme ça ... il utiliserait une approche fonctionnelle plus comme LINQ, la création d' une nouvelle séquence.
Jon Skeet
12
@Stephen Swensen: oui, F # a Seq.iter, mais F # a également des structures de données immuables. lors de l'utilisation de Seq.iter sur ceux-ci, la Seq d'origine n'est pas modifiée; un nouveau Seq avec les éléments secondaires est renvoyé.
Anders
7
@Anders - Seq.iterne renvoie rien, encore moins une nouvelle séquence avec des éléments à effets secondaires. Il s'agit vraiment d'effets secondaires. Vous pensez peut-être Seq.map, car Seq.iterest exactement équivalent à une ForEachméthode d'extension activée IEnumerabe<_>.
Joel Mueller
39

Mise à jour 17/07/2012: Apparemment à partir de C # 5.0, le comportement de foreachdécrit ci-dessous a été modifié et « l'utilisation d'une foreachvariable 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()));
}
drstevens
la source
Que dit l'avertissement de resharper?
reggaeguitar
1
@reggaeguitar Je n'ai pas utilisé C # depuis 7 ou 8 ans, car avant la sortie de C # 5.0, ce qui a changé le comportement décrit ici. Au moment où il a donné cet avertissement stackoverflow.com/questions/1688465/… . Je doute que cela prévienne quand même.
drstevens
34

Vous pouvez utiliser l' FirstOrDefault()extension, qui est disponible pour IEnumerable<T>. En revenant falsedu prédicat, il sera exécuté pour chaque élément mais ne se souciera pas qu'il ne trouve pas réellement de correspondance. Cela évitera les ToList()frais généraux.

IEnumerable<Item> items = GetItems();
items.FirstOrDefault(i => { i.DoStuff(); return false; });
Rhames
la source
11
Je suis d'accord aussi. Ce pourrait être un petit hack intelligent, mais à première vue, ce code n'est pas clair ce qu'il fait, certainement en comparaison avec une boucle foreach standard.
Connell
26
J'ai dû voter contre parce que bien que techniquement correct, votre futur moi et tous vos successeurs vous détesteront pour toujours car au lieu de foreach(Item i in GetItems()) { i.DoStuff();}vous, vous avez pris plus de personnages et rendu extrêmement confus
Mark Sowul
14
Ok, mais prolly un hack plus lisible:items.All(i => { i.DoStuff(); return true; }
nawfal
5
Je sais que c'est un article assez ancien, mais j'ai dû le diminuer également. Je suis d'accord que c'est une astuce intelligente, mais ce n'est pas maintenable. À première vue, il semble que cela ne fasse que le premier élément de la liste. Cela pourrait facilement causer plus de bugs à l'avenir si les codeurs ne comprennent pas comment cela fonctionne et ce qu'il est censé faire.
Lee
4
Il s'agit de la programmation des effets secondaires et à mon humble avis déconseillé.
Anish
21

J'ai pris la méthode de Fredrik et modifié le type de retour.

De cette façon, la méthode prend en charge l' exécution différée comme les autres méthodes LINQ.

EDIT: Si cela n'était pas clair, toute utilisation de cette méthode doit se terminer par ToList () ou toute autre façon de forcer la méthode à travailler sur l'énumérable complet. Sinon, l'action ne serait pas effectué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 pour vous aider à le voir:

[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 supprimez la ToList () à la fin, vous verrez le test échouer puisque le StringBuilder contient une chaîne vide. En effet, aucune méthode n'a forcé ForEach à énumérer.

Dor Rotman
la source
Votre implémentation alternative de ForEachest intéressante, mais elle ne correspond pas au comportement de List.ForEach, dont la signature est public void ForEach(Action<T> action). Il ne correspond pas non plus au comportement de l' Observable.ForEachextension à IObservable<T>, dont la signature est public static void ForEach<TSource>(this IObservable<TSource> source, Action<TSource> onNext). FWIW, l' ForEachéquivalent des collections Scala, même celles qui sont paresseuses, a également un type de retour qui équivaut à void en C #.
drstevens
C'est la meilleure réponse: exécution différée + API fluide.
Alexander Danilov
3
Cela fonctionne exactement comme pour appeler Selectavec ToList. Le but ForEachest de ne pas avoir à appeler ToList. Il devrait s'exécuter immédiatement.
t3chb0t
3
Quel serait l'avantage d'avoir ce "ForEach", dont l'exécution est différée, par rapport à celui qui s'exécute immédiatement? Le report (ce qui n'est pas pratique dans la façon dont ForEach sera utilisé) n'améliore pas le fait que ForEach n'effectue un travail utile que si l'action a des effets secondaires [la plainte habituelle qui est portée contre un tel opérateur Linq]. Alors, comment fait cette aide de changement? De plus, l'ajout de "ToList ()" pour le forcer à s'exécuter ne sert à rien. C'est un accident qui attend de se produire. Le point de "ForEach" est ses effets secondaires. Si vous VOULEZ une énumération retournée, SELECT a plus de sens.
ToolmakerSteve
20

Gardez vos effets secondaires hors de mon IEnumerable

Je voudrais faire l'équivalent de ce qui suit dans LINQ, mais je ne peux pas comprendre comment:

Comme d'autres l'ont souligné ici et à l'étranger, LINQ et les IEnumerableméthodes devraient être exemptes d'effets secondaires.

Voulez-vous vraiment "faire quelque chose" pour chaque élément de l'IEnumerable? C'est alors foreachle meilleur choix. Les gens ne sont pas surpris lorsque des effets secondaires se produisent ici.

foreach (var i in items) i.DoStuff();

Je parie que tu ne veux pas d'effets secondaires

Cependant, d'après mon expérience, les effets secondaires ne sont généralement pas nécessaires. Le plus souvent, il y a une simple requête LINQ à découvrir, accompagnée d'une réponse StackOverflow.com de Jon Skeet, Eric Lippert ou Marc Gravell expliquant comment faire ce que vous voulez!

Quelques exemples

Si vous êtes en train d'agréger (d'accumuler) une certaine valeur, vous devriez envisager la Aggregateméthode d'extension.

items.Aggregate(initial, (acc, x) => ComputeAccumulatedValue(acc, x));

Vous souhaitez peut-être en créer un nouveau à IEnumerablepartir des valeurs existantes.

items.Select(x => Transform(x));

Ou peut-être souhaitez-vous créer une table de correspondance:

items.ToLookup(x, x => GetTheKey(x))

La liste (jeu de mots pas entièrement destiné) de possibilités s'allonge encore et encore.

cdiggins
la source
7
Ah, donc Select est Map et Aggregate est Reduce.
Darrel Lee
17

Si vous voulez agir comme rouleaux d'énumération, vous devez rapporter chaque élément.

public static class EnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
    {
        foreach (var item in enumeration)
        {
            action(item);
            yield return item;
        }
    }
}
regisbsb
la source
Ce n'est pas vraiment un ForEach lorsque vous retournez l'article, c'est plutôt un Tap.
EluciusFTW
10

Il existe une version expérimentale par Microsoft des extensions interactives à LINQ (également sur NuGet , voir le profil de RxTeams pour plus de liens). La vidéo de Channel 9 l' explique bien.

Ses documents sont uniquement fournis au format XML. J'ai exécuté cette documentation à Sandcastle pour lui permettre d'être dans un format plus lisible. Décompressez l'archive des documents et recherchez index.html .

Parmi de nombreux autres avantages, il fournit l'implémentation ForEach attendue. Il vous permet d'écrire du code comme ceci:

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 };

numbers.ForEach(x => Console.WriteLine(x*x));
John Wigger
la source
2
Le lien de travail vers les extensions interactives: microsoft.com/download/en/details.aspx?id=27203
Maciek Świszczowski
8

Selon PLINQ (disponible depuis .Net 4.0), vous pouvez faire un

IEnumerable<T>.AsParallel().ForAll() 

faire une boucle foreach parallèle sur un IEnumerable.

Wolf5
la source
tant que votre action est threadsafe, tout va bien. mais que se passera-t-il si vous avez une action non threadsafe à effectuer? cela peut causer tout un chaos ...
shirbr510
2
Vrai. Ce n'est pas sûr pour les threads, différents threads seront utilisés à partir d'un pool de threads ... Et je dois réviser ma réponse. Il n'y a pas de ForEach () quand je l'essaye maintenant .. Doit être mon code contenant une extension avec ForEach quand j'ai réfléchi à cela.
Wolf5
6

Le but de ForEach est de provoquer des effets secondaires. IEnumerable sert à l'énumération paresseuse d'un ensemble.

Cette différence conceptuelle est bien visible quand on y réfléchit.

SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));

Cela ne s'exécutera pas jusqu'à ce que vous fassiez un "count" ou un "ToList ()" ou quelque chose dessus. Ce n'est clairement pas ce qui est exprimé.

Vous devez utiliser les extensions IEnumerable pour configurer des chaînes d'itération, définissant le contenu par leurs sources et conditions respectives. Les arbres d'expression sont puissants et efficaces, mais vous devez apprendre à apprécier leur nature. Et pas seulement pour la programmation autour d'eux afin de sauvegarder quelques caractères remplaçant l'évaluation paresseuse.

Tormod
la source
5

Beaucoup de gens l'ont mentionné, mais j'ai dû l'écrire. N'est-ce pas le plus clair / le plus lisible?

IEnumerable<Item> items = GetItems();
foreach (var item in items) item.DoStuff();

Court et simple (st).

Nenad
la source
Vous pouvez supprimer toute la première ligne et utiliser GetItems directement dans foreach
tymtam
3
Bien sûr. Il est écrit comme ceci pour plus de clarté. Pour qu'il soit clair quelle GetItems()méthode retourne.
Nenad
4
Maintenant, quand je lis mon commentaire, je vois qu'il semble que je vous dis quelque chose que vous ne savez pas. Je ne voulais pas ça. Ce que je voulais dire, c'est que foreach (var item in GetItems()) item.DoStuff();c'est juste une beauté.
tymtam
4

Maintenant, nous avons la possibilité de ...

        ParallelOptions parallelOptions = new ParallelOptions();
        parallelOptions.MaxDegreeOfParallelism = 4;
#if DEBUG
        parallelOptions.MaxDegreeOfParallelism = 1;
#endif
        Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID));

Bien sûr, cela ouvre une toute nouvelle boîte de ténias.

ps (Désolé pour les polices, c'est ce que le système a décidé)

Paulustrious
la source
4

Comme de nombreuses réponses le soulignent déjà, vous pouvez facilement ajouter vous-même une telle méthode d'extension. Cependant, si vous ne voulez pas faire cela, bien que je ne sois pas au courant de quelque chose comme ça dans la BCL, il y a toujours une option dans l' Systemespace de noms, si vous avez déjà une référence à Reactive Extension (et si vous ne le faites pas , tu aurais dû):

using System.Reactive.Linq;

items.ToObservable().Subscribe(i => i.DoStuff());

Bien que les noms de méthode soient un peu différents, le résultat final est exactement ce que vous recherchez.

Mark Seemann
la source
On dirait que ce package est obsolète maintenant
reggaeguitar
3

ForEach peut également être enchaîné , il suffit de le remettre sur le pileline après l'action. rester fluide


Employees.ForEach(e=>e.Act_A)
         .ForEach(e=>e.Act_B)
         .ForEach(e=>e.Act_C);

Orders  //just for demo
    .ForEach(o=> o.EmailBuyer() )
    .ForEach(o=> o.ProcessBilling() )
    .ForEach(o=> o.ProcessShipping());


//conditional
Employees
    .ForEach(e=> {  if(e.Salary<1000) e.Raise(0.10);})
    .ForEach(e=> {  if(e.Age   >70  ) e.Retire();});

Une version d'implémentation désireuse .

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enu, Action<T> action)
{
    foreach (T item in enu) action(item);
    return enu; // make action Chainable/Fluent
}

Edit: une version Lazy utilise return return, comme ceci .

public static IEnumerable<T> ForEachLazy<T>(this IEnumerable<T> enu, Action<T> action)
{
    foreach (var item in enu)
    {
        action(item);
        yield return item;
    }
}

La version Lazy a besoin d'être matérialisée, ToList () par exemple, sinon, rien ne se passe. voir ci-dessous d'excellents commentaires de ToolmakerSteve.

IQueryable<Product> query = Products.Where(...);
query.ForEachLazy(t => t.Price = t.Price + 1.00)
    .ToList(); //without this line, below SubmitChanges() does nothing.
SubmitChanges();

Je garde à la fois ForEach () et ForEachLazy () dans ma bibliothèque.

Rm558
la source
2
L'avez-vous testé? Il existe plusieurs contextes où cette méthode se comporte de manière contre-intuitive. Mieux seraitforeach(T item in enu) {action(item); yield return item;}
Taemyr
2
Il en résultera plusieurs énumérations
regisbsb
Intéressant. Réfléchir quand je voudrais faire ce qui précède, pour une séquence de 3 actions, plutôt que foreach (T item in enu) { action1(item); action2(item); action3(item); }? Une boucle unique et explicite. Je suppose que s'il était important de faire toutes les actions1 AVANT de commencer à faire les actions2.
ToolmakerSteve
@ Rm558 - votre approche utilisant le "rendement retour". Pro: n'énumère la séquence d'origine qu'une seule fois, donc fonctionne correctement avec un plus large éventail d'énumérations. Con: si vous oubliez ".ToArray ()" à la fin, rien ne se passe. Bien que l'échec soit évident, il ne sera pas négligé pendant longtemps, donc ce ne sera pas un coût majeur. Ne me semble pas "propre", mais c'est le bon compromis, si l'on va dans cette direction (un opérateur ForEach).
ToolmakerSteve
1
Ce code n'est pas sûr du point de vue transactionnel. Et s'il échoue ProcessShipping()? Vous envoyez un e-mail à l'acheteur, chargez sa carte de crédit, mais ne lui envoyez jamais ses affaires? Certainement demander des problèmes.
zmechanic
2

Cette abstraction "d'approche fonctionnelle" fuit beaucoup. Rien au niveau de la langue n'empêche les effets secondaires. Tant que vous pouvez le faire appeler votre lambda / délégué pour chaque élément du conteneur - vous obtiendrez le comportement "ForEach".

Voici par exemple une façon de fusionner srcDictionary en destDictionary (si la clé existe déjà - écrase)

c'est un hack, et ne doit être utilisé dans aucun code de production.

var b = srcDictionary.Select(
                             x=>
                                {
                                  destDictionary[x.Key] = x.Value;
                                  return true;
                                }
                             ).Count();
Zar Shardan
la source
6
Si vous devez ajouter cette clause de non-responsabilité, vous ne devriez probablement pas la publier. Juste mon avis.
Andrew Grothe
2
Comment diable est-ce mieux que ça foreach(var tuple in srcDictionary) { destDictionary[tuple.Key] = tuple.Value; }? Si ce n'est pas dans le code de production, où et pourquoi devriez-vous jamais l'utiliser?
Mark Sowul
3
Ceci juste pour montrer que c'est possible en principe. Il arrive aussi de faire ce que l'OP a demandé, et j'ai clairement indiqué qu'il ne devait pas être utilisé dans la production, toujours curieux et qu'il avait une valeur éducative.
Zar Shardan
2

Inspiré par Jon Skeet, j'ai étendu sa solution avec les éléments suivants:

Méthode d'extension:

public static void Execute<TSource, TKey>(this IEnumerable<TSource> source, Action<TKey> applyBehavior, Func<TSource, TKey> keySelector)
{
    foreach (var item in source)
    {
        var target = keySelector(item);
        applyBehavior(target);
    }
}

Client:

var jobs = new List<Job>() 
    { 
        new Job { Id = "XAML Developer" }, 
        new Job { Id = "Assassin" }, 
        new Job { Id = "Narco Trafficker" }
    };

jobs.Execute(ApplyFilter, j => j.Id);

. . .

    public void ApplyFilter(string filterId)
    {
        Debug.WriteLine(filterId);
    }
Scott Nimrod
la source
1

Je suis en désaccord avec la notion selon laquelle les méthodes d'extension de lien devraient être sans effet secondaire (non seulement parce qu'elles ne le sont pas, tout délégué peut effectuer des effets secondaires).

Considérer ce qui suit:

   public class Element {}

   public Enum ProcessType
   {
      This = 0, That = 1, SomethingElse = 2
   }

   public class Class1
   {
      private Dictionary<ProcessType, Action<Element>> actions = 
         new Dictionary<ProcessType,Action<Element>>();

      public Class1()
      {
         actions.Add( ProcessType.This, DoThis );
         actions.Add( ProcessType.That, DoThat );
         actions.Add( ProcessType.SomethingElse, DoSomethingElse );
      }

      // Element actions:

      // This example defines 3 distict actions
      // that can be applied to individual elements,
      // But for the sake of the argument, make
      // no assumption about how many distict
      // actions there may, and that there could
      // possibly be many more.

      public void DoThis( Element element )
      {
         // Do something to element
      }

      public void DoThat( Element element )
      {
         // Do something to element
      }

      public void DoSomethingElse( Element element )
      {
         // Do something to element
      }

      public void Apply( ProcessType processType, IEnumerable<Element> elements )
      {
         Action<Element> action = null;
         if( ! actions.TryGetValue( processType, out action ) )
            throw new ArgumentException("processType");
         foreach( element in elements ) 
            action(element);
      }
   }

Ce que l'exemple montre est vraiment juste une sorte de liaison tardive qui permet d'invoquer l'une des nombreuses actions possibles ayant des effets secondaires sur une séquence d'éléments, sans avoir à écrire une construction de gros commutateur pour décoder la valeur qui définit l'action et traduire dans sa méthode correspondante.

Caddzooks 2 tours
la source
9
Il y a une différence entre si une méthode d'extension PEUT avoir des effets secondaires et si elle DEVRAIT en avoir. Vous faites seulement remarquer que cela peut avoir, alors qu'il peut y avoir de très bonnes raisons de proposer qu'ils ne devraient pas avoir d'effets secondaires. Dans la programmation fonctionnelle, toutes les fonctions sont exemptes d'effets secondaires, et lorsque vous utilisez des constructions de programmation fonctionnelle, vous pouvez supposer qu'elles le sont.
Dave Van den Eynde
1

Pour rester fluide, on peut utiliser une telle astuce:

GetItems()
    .Select(i => new Action(i.DoStuf)))
    .Aggregate((a, b) => a + b)
    .Invoke();
Alex
la source
0

Pour VB.NET, vous devez utiliser:

listVariable.ForEach(Sub(i) i.Property = "Value")
Israël Margulies
la source
REMARQUE: cela se compile uniquement si vous avez un Listou IList. Pour un général IEnumerable, écrivez l'équivalent VB de la méthode d'extension ForEach de la réponse acceptée .
ToolmakerSteve
-1

Encore un autre ForEachexemple

public static IList<AddressEntry> MapToDomain(IList<AddressModel> addresses)
{
    var workingAddresses = new List<AddressEntry>();

    addresses.Select(a => a).ToList().ForEach(a => workingAddresses.Add(AddressModelMapper.MapToDomain(a)));

    return workingAddresses;
}
neil martin
la source
4
Quel gaspillage de mémoire. Cela appelle ToList pour ajouter à une liste. Pourquoi cela ne renvoie-t-il pas simplement les adresses.Sélectionnez (a => MapToDomain (a))?
Mark Sowul