Question

  

Double possible:
   Pourquoi n'y a-t-il pas de méthode d'extension ForEach? sur l'interface IEnumerable?

Lors de l'écriture du code LINQ-y, j'ai remarqué que .ForEach () est un idiot à utiliser. Par exemple, voici un morceau de code qui prend les entrées suivantes et produit ces sorties:

{ "One" } => "One"
{ "One", "Two" } => "One, Two"
{ "One", "Two", "Three", "Four" } => "One, Two, Three and Four";

Et le code:

private string InsertCommasAttempt(IEnumerable<string> words)
{
    List<string> wordList = words.ToList();
    StringBuilder sb = new StringBuilder();
    var wordsAndSeparators = wordList.Select((string word, int pos) =>
        {
            if (pos == 0) return new { Word = word, Leading = string.Empty };
            if (pos == wordList.Count - 1) return new { Word = word, Leading = " and " };
            return new { Word = word, Leading = ", " };
        });

    wordsAndSeparators.ToList().ForEach(v => sb.Append(v.Leading).Append(v.Word));
    return sb.ToString();
}

Notez le .ToList () interjecté avant le .ForEach () à l'avant-dernière ligne.

Pourquoi .ForEach () n'est-il pas disponible en tant que méthode d'extension sur IEnumerable < T > ? Avec un exemple comme celui-ci, cela semble simplement bizarre.

Était-ce utile?

La solution

Parce que ForEach (Action) existait avant < IEnumerable < T > .

Etant donné qu’elle n’a pas été ajoutée aux autres méthodes d’extension, on peut supposer que les concepteurs de C # ont estimé qu’il s’agissait d’une mauvaise conception et ont préféré la construction foreach .

Modifier:

Si vous le souhaitez, vous pouvez créer votre propre méthode d'extension, elle ne remplacera pas celle d'un List < T > , mais fonctionnera pour toute autre classe implémentant IEnumerable < T >. ; .

public static class IEnumerableExtensions
{
  public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
  {
    foreach (T item in source)
      action(item);
  }
}

Autres conseils

Selon Eric Lippert, il s’agit principalement pour des raisons philosophiques . Vous devriez lire le post en entier, mais voici l'essentiel en ce qui me concerne:

  

Je suis philosophiquement opposé à   fournissant une telle méthode, pour deux   raisons.

     

La première raison est que cela   viole la programmation fonctionnelle   principes que toute autre séquence   les opérateurs sont basés sur. Clairement le   seul but d'un appel à cette méthode   est de provoquer des effets secondaires.

     

Le but d'une expression est de   calculer une valeur, ne pas causer un côté   effet. Le but d'une déclaration est   provoquer un effet secondaire. Le site d'appel   de cette chose ressemblerait énormément   comme une expression (bien que,   certes, puisque la méthode est   retour vide, l'expression pourrait   être utilisé que dans une “déclaration   expression "contexte.)

     

Cela ne me convient pas de faire   le seul et unique opérateur de séquence   cela n'est utile que pour son côté   effets.

     

La deuxième raison est que cela   ajoute zéro nouveau pouvoir de représentation   à la langue.

Parce que ForEach () sur un IEnumerable est simplement une normale pour chaque boucle comme celle-ci:

for each T item in MyEnumerable
{
    // Action<T> goes here
}

Je ne fais que deviner, mais mettre foreach sur IEnumerable aurait pour conséquence que ses opérations auraient des effets secondaires. Aucune des options "disponible" les méthodes d'extension causent des effets secondaires, mettre une méthode impérative comme foreach brouillerait l'api, je suppose. Foreach initialisera également la collection paresseuse.

Personnellement, j’ai repoussé la tentation d’ajouter la mienne, juste pour que les fonctions sans effets secondaires soient séparées de celles comportant des effets secondaires.

ForEach n'est pas sur IList, il est sur la liste. Vous utilisiez la liste concrète dans votre exemple.

Honnêtement, je ne sais vraiment pas pourquoi le .ForEach (Action) n’est pas inclus dans IEnumerable, mais, bon, faux ou indifférent, c’est comme ça ...

Je voulais toutefois souligner le problème de performances mentionné dans d'autres commentaires. La performance dépend de la manière dont vous passez en boucle sur une collection. C'est relativement mineur mais néanmoins, il existe certainement. Voici un extrait de code incroyablement rapide et bâclé pour montrer les relations ... cela ne prend que quelques minutes à parcourir.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Start Loop timing test: loading collection...");
        List<int> l = new List<int>();

        for (long i = 0; i < 60000000; i++)
        {
            l.Add(Convert.ToInt32(i));
        }

        Console.WriteLine("Collection loaded with {0} elements: start timings",l.Count());
        Console.WriteLine("\n<===============================================>\n");
        Console.WriteLine("foreach loop test starting...");

        DateTime start = DateTime.Now;

        //l.ForEach(x => l[x].ToString());

        foreach (int x in l)
            l[x].ToString();

        Console.WriteLine("foreach Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
        Console.WriteLine("\n<===============================================>\n");
        Console.WriteLine("List.ForEach(x => x.action) loop test starting...");

        start = DateTime.Now;

        l.ForEach(x => l[x].ToString());

        Console.WriteLine("List.ForEach(x => x.action) Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
        Console.WriteLine("\n<===============================================>\n");

        Console.WriteLine("for loop test starting...");

        start = DateTime.Now;
        int count = l.Count();
        for (int i = 0; i < count; i++)
        {
            l[i].ToString();
        }

        Console.WriteLine("for Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
        Console.WriteLine("\n<===============================================>\n");

        Console.WriteLine("\n\nPress Enter to continue...");
        Console.ReadLine();
    }

Ne vous attardez pas trop dessus cependant. La performance est la devise de la conception d’application, mais à moins que votre application ne rencontre des problèmes de performances qui posent des problèmes d’utilisabilité, concentrez-vous sur le codage pour la maintenabilité et la réutilisation, car la durée est la devise des projets professionnels réels.

ForEach est implémenté dans la classe concrète Liste < T >

Juste une supposition, mais List peut parcourir ses éléments sans créer d'énumérateur:

public void ForEach(Action<T> action)
{
    if (action == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        action(this._items[i]);
    }
}

Cela peut conduire à de meilleures performances. Avec IEnumerable, vous n’avez pas la possibilité d’utiliser une boucle for ordinaire.

Cela s'appelle "Sélectionner". sur IEnumerable < T > Je suis éclairé, merci.

LINQ suit le modèle d'extraction et toutes ses méthodes (d'extension) doivent renvoyer IEnumerable < T > , à l'exception de ToList () . Le ToList () est là pour mettre fin à la chaîne d'extraction.

ForEach () provient du monde des modèles push.

Vous pouvez toujours écrire votre propre méthode d'extension pour le faire, comme l'a souligné Samuel.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top