Inspiriert von einer anderen Frage nach der fehlenden Zip
Funktion:
Warum gibt es ForEach
in der Enumerable
Klasse keine Erweiterungsmethode ? Oder irgendwo? Die einzige Klasse, die eine ForEach
Methode erhält , ist List<>
. Gibt es einen Grund, warum es fehlt (Leistung)?
c#
.net
vb.net
extension-methods
Cameron MacFarland
quelle
quelle
Parallel.ForEach
umEnumerable.ForEach
wiederzuentdecken, dass letzterer nicht existiert. C # hat einen Trick verpasst, um es hier einfach zu machen.Antworten:
In der Sprache, die die meiste Zeit die Arbeit erledigt, ist bereits eine foreach-Anweisung enthalten.
Ich würde es hassen, Folgendes zu sehen:
Anstatt:
Letzteres ist in den meisten Situationen klarer und leichter zu lesen , obwohl es möglicherweise etwas länger zu tippen ist.
Ich muss jedoch zugeben, dass ich meine Haltung zu diesem Thema geändert habe. Eine ForEach () - Erweiterungsmethode wäre in einigen Situationen tatsächlich nützlich.
Hier sind die Hauptunterschiede zwischen der Aussage und der Methode:
Das sind alles großartige Punkte, die von vielen Leuten hier gemacht wurden, und ich kann sehen, warum den Leuten die Funktion fehlt. Es würde mir nichts ausmachen, wenn Microsoft in der nächsten Framework-Iteration eine Standard-ForEach-Methode hinzufügt.
quelle
foreach
wird beim Kompilieren typgeprüft, wenn die Aufzählung parametriert ist. Es fehlt nur die Überprüfung des Typs der Kompilierungszeit für nicht generische Sammlungen, wie dies bei einer hypothetischenForEach
Erweiterungsmethode der Fall wäre.col.Where(...).OrderBy(...).ForEach(...)
. Viel einfachere Syntax, keine Bildschirmverschmutzung mit redundanten lokalen Variablen oder langen Klammern in foreach ().list.ForEach(item => item.DoSomething())
. Und wer sagt, dass es eine Liste ist? Der offensichtliche Anwendungsfall befindet sich am Ende einer Kette. Mit der foreach-Anweisung müssten Sie die gesamte Kette nachin
und die auszuführende Operation innerhalb des Blocks platzieren, was weitaus weniger natürlich oder konsistent ist.ForEach-Methode wurde vor LINQ hinzugefügt. Wenn Sie die ForEach-Erweiterung hinzufügen, wird sie aufgrund von Einschränkungen der Erweiterungsmethoden niemals für List-Instanzen aufgerufen. Ich denke, der Grund, warum es nicht hinzugefügt wurde, ist, dass es nicht in bestehende eingreift.
Wenn Sie diese kleine nette Funktion jedoch wirklich vermissen, können Sie Ihre eigene Version herausbringen
quelle
Sie könnten diese Erweiterungsmethode schreiben:
Vorteile
Ermöglicht Verkettung:
Nachteile
Es wird eigentlich nichts tun, bis Sie etwas tun, um die Iteration zu erzwingen. Aus diesem Grund sollte es nicht aufgerufen werden
.ForEach()
. Sie könnten.ToList()
am Ende schreiben , oder Sie könnten auch diese Erweiterungsmethode schreiben:Dies kann eine zu bedeutende Abweichung von den Versand-C # -Bibliotheken sein. Leser, die mit Ihren Erweiterungsmethoden nicht vertraut sind, wissen nicht, was sie mit Ihrem Code anfangen sollen.
quelle
{foreach (var e in source) {action(e);} return source;}
numbers.Select(n => n*2);
Select()
heißt es, eine Funktion auf jedes Element anwenden und den Wert der Funktion zurückgeben, wobeiApply()
besagt, dassAction
auf jedes Element eine angewendet wird und das ursprüngliche Element zurückgegeben wird.Select(e => { action(e); return e; })
Die Diskussion hier gibt die Antwort:
Grundsätzlich wurde die Entscheidung getroffen, die Erweiterungsmethoden funktional "rein" zu halten. Ein ForEach würde bei Verwendung der Enumerable-Erweiterungsmethoden zu Nebenwirkungen führen, was nicht beabsichtigt war.
quelle
Obwohl ich der Meinung bin, dass es
Beispielforeach
in den meisten Fällen besser ist, das integrierte Konstrukt zu verwenden, finde ich die Verwendung dieser Variante der ForEach <> -Erweiterung etwas netter, als den Indexforeach
selbst regelmäßig verwalten zu müssen :Würde dir geben:
quelle
Eine Problemumgehung ist das Schreiben
.ToList().ForEach(x => ...)
.Profis
Einfach zu verstehen - der Leser muss nur wissen, was mit C # geliefert wird, keine zusätzlichen Erweiterungsmethoden.
Syntaktisches Rauschen ist sehr mild (fügt nur ein wenig extranious Code hinzu).
Kostet normalerweise keinen zusätzlichen Speicher, da ein Eingeborener
.ForEach()
sowieso die gesamte Sammlung realisieren müsste.Nachteile
Die Reihenfolge der Operationen ist nicht ideal. Ich würde lieber ein Element erkennen, dann darauf reagieren und es dann wiederholen. Dieser Code realisiert zuerst alle Elemente und wirkt dann nacheinander auf sie ein.
Wenn das Realisieren der Liste eine Ausnahme auslöst, können Sie niemals auf ein einzelnes Element einwirken.
Wenn die Aufzählung unendlich ist (wie die natürlichen Zahlen), haben Sie kein Glück.
quelle
Enumerable.ForEach<T>
Methode gibt, aber eineList<T>.ForEach
Methode. c) "Kostet normalerweise keinen zusätzlichen Speicher, da ein nativer .ForEach () sowieso die gesamte Sammlung realisieren müsste." - vollständiger und völliger Unsinn. Hier ist die Implementierung von Enumerable.ForEach <T>, die C # bereitstellen würde, wenn dies der Fall wäre:public static void ForEach<T>(this IEnumerable<T> list, Action<T> action) { foreach (T item in list) action(item); }
Ich habe mich immer gefragt, warum ich das immer bei mir habe:
Schöne kleine Verlängerungsmethode.
quelle
Daher gab es viele Kommentare dazu, dass eine ForEach-Erweiterungsmethode nicht geeignet ist, da sie keinen Wert wie die LINQ-Erweiterungsmethoden zurückgibt. Dies ist zwar eine sachliche Aussage, aber nicht ganz richtig.
Die LINQ-Erweiterungsmethoden geben alle einen Wert zurück, damit sie miteinander verkettet werden können:
Nur weil LINQ mithilfe von Erweiterungsmethoden implementiert wird, bedeutet dies nicht, dass Erweiterungsmethoden erforderlich sind in gleicher Weise und liefern einen Wert verwendet werden. Das Schreiben einer Erweiterungsmethode zum Offenlegen allgemeiner Funktionen, die keinen Wert zurückgeben, ist eine absolut gültige Verwendung.
Das spezifische Argument für ForEach ist, dass es basierend auf den Einschränkungen für Erweiterungsmethoden (nämlich dass eine Erweiterungsmethode niemals eine geerbte Methode mit derselben Signatur überschreibt ) eine Situation geben kann, in der die benutzerdefinierte Erweiterungsmethode für alle Klassen verfügbar ist, die impelement verwenden IEnumerable
<T
> außer List<T
>. Dies kann zu Verwirrung führen, wenn sich die Methoden unterschiedlich verhalten, je nachdem, ob die Erweiterungsmethode oder die Vererbungsmethode aufgerufen wird.quelle
Sie können das (verkettbar, aber träge ausgewertet) verwenden
Select
, indem Sie zuerst Ihre Operation ausführen und dann die Identität zurückgeben (oder etwas anderes, wenn Sie es vorziehen).Sie müssen sicherstellen, dass es immer noch ausgewertet wird, entweder mit
Count()
(der billigsten Operation zum Aufzählen von afaik) oder einer anderen Operation, die Sie sowieso benötigen.Ich würde es gerne in der Standardbibliothek sehen:
Der obige Code wird dann
people.WithLazySideEffect(p => Console.WriteLine(p))
effektiv zu foreach, aber faul und verkettbar.quelle
Select
macht der nicht mehr das, was er tun soll. Es ist definitiv eine Lösung für das, was das OP verlangt - aber zum Thema Lesbarkeit: Niemand würde erwarten, dass die Auswahl eine Funktion ausführt.Select
nicht das getan wird, was es tun soll, ist dies ein Problem bei der Implementierung vonSelect
, nicht bei dem aufrufenden CodeSelect
, der keinen Einfluss darauf hat, was zu tunSelect
ist.Beachten Sie, dass MoreLINQ NuGet die gesuchte
ForEach
Erweiterungsmethode bereitstellt (sowie einePipe
Methode, die den Delegaten ausführt und dessen Ergebnis liefert). Sehen:quelle
@Coincoin
Die wahre Stärke der foreach-Erweiterungsmethode besteht in der Wiederverwendbarkeit der,
Action<>
ohne unnötige Methoden zu Ihrem Code hinzuzufügen. Angenommen, Sie haben 10 Listen und möchten dieselbe Logik für sie ausführen, und eine entsprechende Funktion passt nicht in Ihre Klasse und wird nicht wiederverwendet. Anstatt zehn for-Schleifen oder eine generische Funktion zu haben, die offensichtlich ein Helfer ist, der nicht dazu gehört, können Sie Ihre gesamte Logik an einem Ort aufbewahren (Action<>
also werden Dutzende von Zeilen durch ersetztusw...
Die Logik befindet sich an einem Ort und Sie haben Ihre Klasse nicht verschmutzt.
quelle
f(p)
, lassen Sie die{ }
Kurzform weg :for (var p in List1.Concat(List2).Concat(List2)) f(p);
.Die meisten LINQ-Erweiterungsmethoden geben Ergebnisse zurück. ForEach passt nicht in dieses Muster, da es nichts zurückgibt.
quelle
public IEnumerable<T> Foreach<T>(this IEnumerable<T> items, Action<T> action) { /* error check */ foreach (var item in items) { action(item); yield return item; } }
Wenn Sie F # haben (das in der nächsten Version von .NET enthalten sein wird), können Sie verwenden
Seq.iter doSomething myIEnumerable
quelle
Teilweise liegt es daran, dass die Sprachdesigner aus philosophischer Sicht nicht damit einverstanden sind.
https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
quelle
Sie können select verwenden, wenn Sie etwas zurückgeben möchten. Wenn Sie dies nicht tun, können Sie zuerst ToList verwenden, da Sie wahrscheinlich nichts in der Sammlung ändern möchten.
quelle
Ich habe einen Blog-Beitrag darüber geschrieben: http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx
Sie können hier abstimmen, wenn Sie diese Methode in .NET 4.0 sehen möchten : http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093
quelle
In 3.5 sind alle zu IEnumerable hinzugefügten Erweiterungsmethoden für die LINQ-Unterstützung vorhanden (beachten Sie, dass sie in der System.Linq.Enumerable-Klasse definiert sind). In diesem Beitrag erkläre ich, warum foreach nicht zu LINQ gehört: Vorhandene LINQ-Erweiterungsmethode ähnlich wie Parallel.For?
quelle
Bin ich es oder ist die Liste <T> .Foreach wurde von Linq so ziemlich obsolet gemacht. Ursprünglich gab es
Dabei musste Y einfach IEnumerable (Pre 2.0) sein und einen GetEnumerator () implementieren. Wenn Sie sich die generierte MSIL ansehen, sehen Sie, dass sie genau dieselbe ist wie
(Siehe http://alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx für die MSIL)
Dann kamen in DotNet2.0 Generics und die Liste. Foreach war für mich immer eine Implementierung des Vistor-Musters (siehe Design Patterns von Gamma, Helm, Johnson, Vlissides).
Jetzt können wir in 3.5 natürlich stattdessen ein Lambda mit dem gleichen Effekt verwenden. Beispiel: http://dotnet-developments.blogs.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh- meine/
quelle
Ich möchte Akus Antwort erweitern .
Wenn Sie eine Methode nur zum Zweck ihrer Nebenwirkung aufrufen möchten, ohne zuerst die gesamte Aufzählung zu iterieren, können Sie Folgendes verwenden:
quelle
Select(x => { f(x); return x;})
Bisher hat noch niemand darauf hingewiesen, dass ForEach <T> zu einer Überprüfung des Typs der Kompilierungszeit führt, bei der das Schlüsselwort foreach zur Laufzeit überprüft wird.
Nachdem ich einige Umgestaltungen vorgenommen habe, bei denen beide Methoden im Code verwendet wurden, bevorzuge ich .ForEach, da ich Testfehler / Laufzeitfehler suchen musste, um die foreach-Probleme zu finden.
quelle
foreach
viel weniger Kompilierungszeit überprüft wird. Sicher, wenn Ihre Sammlung eine nicht generische IEnumerable ist, ist die Schleifenvariable eineobject
, aber das Gleiche gilt für eine hypothetischeForEach
Erweiterungsmethode.