Pourquoi .ForEach () est-il sur IList < T > et pas sur IEnumerable < T > ;? [dupliquer]
-
03-07-2019 - |
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.
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
Je suis éclairé, merci. IEnumerable < T >
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.