web-dev-qa-db-fra.com

Pourquoi .ForEach () sur IList <T> et non sur IEnumerable <T>?

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

J'ai remarqué lors de l'écriture du code LINQ-y que .ForEach() est un idiome agréable à 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 la .ToList() interrompue avant la .ForEach() sur l'avant-dernière ligne.

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

52
Olema

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

Puisqu'il n'a pas été ajouté avec les autres méthodes d'extension, on peut supposer que les concepteurs C # ont estimé que c'était une mauvaise conception et préfèrent la construction foreach.


Éditer:

Si vous voulez, vous pouvez créer votre propre méthode d'extension, elle ne remplacera pas celle d'un List<T> mais cela fonctionnera pour toute autre classe qui implémente 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);
  }
}
38
Samuel

Selon Eric Lippert, c'est principalement pour des raisons philosophiques . Vous devriez lire l'intégralité du message, mais voici l'essentiel en ce qui me concerne:

Je suis philosophiquement opposé à la fourniture d'une telle méthode, pour deux raisons.

La première raison est que cela viole les principes de programmation fonctionnelle sur lesquels tous les autres opérateurs de séquence sont basés. De toute évidence, 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, et non de provoquer un effet secondaire. Le but d'une déclaration est de provoquer un effet secondaire. Le site d'appel de cette chose ressemblerait énormément à une expression (bien que, certes, puisque la méthode renvoie un vide, l'expression ne peut être utilisée que dans un contexte d '"expression expression".)

Il ne me convient pas bien de créer le seul et unique opérateur de séquence qui ne soit utile que pour ses effets secondaires.

La deuxième raison est que cela n'ajoute aucun nouveau pouvoir de représentation au langage.

39
Justin R.

Parce que ForEach() sur un IEnumerable est juste une normale pour chaque boucle comme ceci:

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

Je suis juste en train de deviner ici, mais mettre foreach sur IEnumerable entraînerait des opérations sur lui pour avoir des effets secondaires. Aucune des méthodes d'extension "disponibles" ne provoque d'effets secondaires, mettre une méthode impérative comme foreach dessus brouillerait l'api je suppose. De plus, foreach initialiserait la collection paresseuse.

Personnellement, j'ai repoussé la tentation d'ajouter simplement les miennes, juste pour garder les fonctions sans effet secondaire séparées de celles avec des effets secondaires.

3
Surya

Honnêtement, je ne sais pas avec certitude pourquoi le .ForEach (Action) n'est pas inclus sur IEnumerable mais, à droite, à tort ou indifférent, c'est ainsi ...

Je DID cependant je veux souligner le problème de performance mentionné dans d'autres commentaires. Il y a un hit de performance basé sur la façon dont vous bouclez sur une collection. Il est relativement mineur mais néanmoins, il existe certainement. Voici un extrait de code incroyablement rapide et bâclé pour montrer les relations ... ne prend qu'une minute ou deux à 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();
    }

Mais ne vous y attardez pas trop. Les performances sont la devise de la conception des applications, mais à moins que votre application ne connaisse un réel impact sur les performances qui cause des problèmes d'utilisation, concentrez-vous sur le codage pour la maintenabilité et la réutilisation, car le temps est la devise des projets commerciaux réels ...

2
Aaron

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

2
Joshua Belden

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

0
Chad Grant

Il s'appelle "Select" sur IEnumerable<T> Je suis éclairé, merci.

0
JP Alioto

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.

0
Rauhotz

LINQ suit le modèle pull et toutes ses méthodes (d'extension) doivent renvoyer IEnumerable<T>, À l'exception de ToList(). La ToList() est là pour terminer la chaîne de traction.

ForEach() est issue du monde des modèles Push.

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

0
tofi9