Warum gibt es in IEnumerable keine ForEach-Erweiterungsmethode?

389

Inspiriert von einer anderen Frage nach der fehlenden ZipFunktion:

Warum gibt es ForEachin der EnumerableKlasse keine Erweiterungsmethode ? Oder irgendwo? Die einzige Klasse, die eine ForEachMethode erhält , ist List<>. Gibt es einen Grund, warum es fehlt (Leistung)?

Cameron MacFarland
quelle
Es ist wahrscheinlich, wie in einem früheren Beitrag angedeutet, dass foreach als Sprachschlüsselwort in C # und VB.NET (und vielen anderen) unterstützt wird. Die meisten Leute (ich selbst eingeschlossen) schreiben einfach eines nach Bedarf und angemessen.
Chrisb
2
Verwandte Frage hier mit Link zu 'offizielle Antwort' stackoverflow.com/questions/858978/…
Benjol
Ich denke, ein sehr wichtiger Punkt ist, dass eine foreach-Schleife leichter zu erkennen ist. Wenn Sie .ForEach (...) verwenden, ist es leicht, diesen zu übersehen, wenn Sie den Code durchsehen. Dies ist wichtig, wenn Sie Leistungsprobleme haben. Natürlich haben .ToList () und .ToArray () das gleiche Problem, aber sie werden etwas anders verwendet. Ein weiterer Grund könnte sein, dass es in .ForEach üblicher ist, die Quellliste zu ändern (z. B. Elemente entfernen / hinzufügen), sodass es sich nicht mehr um eine "reine" Abfrage handelt.
Daniel Bişar
Beim Debuggen von parallelisiertem Code versuche ich oft, nur zu tauschen, Parallel.ForEachum Enumerable.ForEachwiederzuentdecken, dass letzterer nicht existiert. C # hat einen Trick verpasst, um es hier einfach zu machen.
Colonel Panic

Antworten:

198

In der Sprache, die die meiste Zeit die Arbeit erledigt, ist bereits eine foreach-Anweisung enthalten.

Ich würde es hassen, Folgendes zu sehen:

list.ForEach( item =>
{
    item.DoSomething();
} );

Anstatt:

foreach(Item item in list)
{
     item.DoSomething();
}

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:

  • Typprüfung: foreach wird zur Laufzeit ausgeführt, ForEach () befindet sich zur Kompilierungszeit (Big Plus!)
  • Die Syntax zum Aufrufen eines Delegaten ist in der Tat viel einfacher: objects.ForEach (DoSomething);
  • ForEach () könnte verkettet werden: obwohl die Bösartigkeit / Nützlichkeit eines solchen Features zur Diskussion steht.

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.

Münze
quelle
39
Die Diskussion hier gibt die Antwort: forums.microsoft.com/MSDN/… Grundsätzlich wurde die Entscheidung getroffen, die Erweiterungsmethoden funktional "rein" zu halten. Ein ForEach würde Nebenwirkungen bei der Verwendung der Erweiterungsmethoden fördern, was nicht beabsichtigt war.
Mancaus
17
'foreach' mag klarer erscheinen, wenn Sie einen C-Hintergrund haben, aber die interne Iteration gibt Ihre Absicht klarer an. Das heißt, Sie könnten Dinge wie 'sequence.AsParallel (). ForEach (...)' tun.
Jay Bazuzi
10
Ich bin mir nicht sicher, ob Ihr Standpunkt zur Typprüfung richtig ist. foreachwird 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 hypothetischen ForEachErweiterungsmethode der Fall wäre.
Richard Poole
49
Die Erweiterungsmethode wäre in Ketten-LINQ mit Punktnotation sehr nützlich, wie : col.Where(...).OrderBy(...).ForEach(...). Viel einfachere Syntax, keine Bildschirmverschmutzung mit redundanten lokalen Variablen oder langen Klammern in foreach ().
Jacek Gorgoń
22
"Ich würde es hassen, Folgendes zu sehen" - Die Frage bezog sich nicht auf Ihre persönlichen Vorlieben, und Sie haben den Code hasserfüllter gemacht, indem Sie überflüssige Zeilenvorschübe und ein überflüssiges Paar Klammern hinzugefügt haben. Nicht so hasserfüllt ist 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 nach inund die auszuführende Operation innerhalb des Blocks platzieren, was weitaus weniger natürlich oder konsistent ist.
Jim Balter
73

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

public static void ForEach<T>(
    this IEnumerable<T> source,
    Action<T> action)
{
    foreach (T element in source) 
        action(element);
}
aku
quelle
11
Die Erweiterungsmethoden ermöglichen dies. Wenn bereits eine Instanzimplementierung eines bestimmten Methodennamens vorhanden ist, wird diese Methode anstelle der Erweiterungsmethode verwendet. Sie können also eine Erweiterungsmethode ForEach implementieren und sie nicht mit der ForEach-Methode List <> kollidieren lassen.
Cameron MacFarland
3
Cameron, glauben Sie mir, ich weiß, wie Erweiterungsmethoden funktionieren :) List erbt IEnumerable, so dass es nicht offensichtlich ist.
Aku
9
Aus dem Programmierhandbuch für Erweiterungsmethoden ( msdn.microsoft.com/en-us/library/bb383977.aspx ): Eine Erweiterungsmethode mit demselben Namen und derselben Signatur wie eine Schnittstellen- oder Klassenmethode wird niemals aufgerufen. Scheint mir ziemlich offensichtlich.
Cameron MacFarland
3
Spreche ich etwas anderes? Ich denke, es ist nicht gut, wenn viele Klassen die ForEach-Methode haben, aber einige von ihnen funktionieren möglicherweise anders.
Aku
5
Eine interessante Sache, die Sie bei List <> beachten sollten. ForEach und Array.ForEach ist, dass sie das foreach-Konstrukt nicht intern verwenden. Beide verwenden eine Plain-for-Schleife. Vielleicht ist es eine Performance-Sache ( diditwith.net/2006/10/05/PerformanceOfForeachVsListForEach.aspx ). Angesichts der Tatsache, dass sowohl Array als auch List ForEach-Methoden implementieren, ist es überraschend, dass sie zumindest keine Erweiterungsmethode für IList <> implementiert haben, wenn nicht auch für IEnumerable <>.
Matt Dotson
53

Sie könnten diese Erweiterungsmethode schreiben:

// Possibly call this "Do"
IEnumerable<T> Apply<T> (this IEnumerable<T> source, Action<T> action)
{
    foreach (var e in source)
    {
        action(e);
        yield return e;
    }
}

Vorteile

Ermöglicht Verkettung:

MySequence
    .Apply(...)
    .Apply(...)
    .Apply(...);

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:

// possibly call this "Realize"
IEnumerable<T> Done<T> (this IEnumerable<T> source)
{
    foreach (var e in source)
    {
        // do nothing
        ;
    }

    return source;
}

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.

Jay Bazuzi
quelle
1
Ihre erste Funktion könnte ohne die ' {foreach (var e in source) {action(e);} return source;}
Realise'
3
Könnten Sie nicht einfach Select anstelle Ihrer Apply-Methode verwenden? Es scheint mir, dass das Anwenden einer Aktion auf jedes Element und das Ausgeben genau das ist, was Select tut. ZBnumbers.Select(n => n*2);
rasmusvhansen
1
Ich denke, Apply ist besser lesbar als Select für diesen Zweck. Wenn ich eine Select-Anweisung lese, lese ich sie als "etwas von innen holen", wobei "Anwenden" "etwas Arbeit erledigen" bedeutet.
Dr. Rob Lang
2
@rasmusvhansen Tatsächlich Select()heißt es, eine Funktion auf jedes Element anwenden und den Wert der Funktion zurückgeben, wobei Apply()besagt, dass Actionauf jedes Element eine angewendet wird und das ursprüngliche Element zurückgegeben wird.
NetMage
1
Dies ist gleichbedeutend mitSelect(e => { action(e); return e; })
Jim Balter
35

Die Diskussion hier gibt die Antwort:

Tatsächlich hing die spezifische Diskussion, die ich miterlebte, tatsächlich von der funktionalen Reinheit ab. In einem Ausdruck werden häufig Annahmen getroffen, dass keine Nebenwirkungen auftreten. ForEach zu haben, lädt gezielt zu Nebenwirkungen ein, anstatt sie nur in Kauf zu nehmen. - Keith Farmer (Partner)

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.

Mancaus
quelle
6
Siehe auch blogs.msdn.com/b/ericlippert/archive/2009/05/18/… . Dies verstößt so gegen die funktionalen Programmierprinzipien, auf denen alle anderen Sequenzoperatoren basieren. Der einzige Zweck eines Aufrufs dieser Methode besteht eindeutig darin, Nebenwirkungen zu verursachen
Michael Freidgeim
7
Diese Argumentation ist völlig falsch. Der Anwendungsfall ist, dass Nebenwirkungen erforderlich sind ... ohne ForEach müssen Sie eine foreach-Schleife schreiben. Die Existenz von ForEach fördert keine Nebenwirkungen und ihre Abwesenheit verringert sie nicht.
Jim Balter
Angesichts der Einfachheit des Schreibens der Erweiterungsmethode gibt es buchstäblich keinen Grund, warum dies kein Sprachstandard ist.
Kapitän Prinny
17

Obwohl ich der Meinung bin, dass es foreachin den meisten Fällen besser ist, das integrierte Konstrukt zu verwenden, finde ich die Verwendung dieser Variante der ForEach <> -Erweiterung etwas netter, als den Index foreachselbst regelmäßig verwalten zu müssen :

public static int ForEach<T>(this IEnumerable<T> list, Action<int, T> action)
{
    if (action == null) throw new ArgumentNullException("action");

    var index = 0;

    foreach (var elem in list)
        action(index++, elem);

    return index;
}
Beispiel
var people = new[] { "Moe", "Curly", "Larry" };
people.ForEach((i, p) => Console.WriteLine("Person #{0} is {1}", i, p));

Würde dir geben:

Person #0 is Moe
Person #1 is Curly
Person #2 is Larry
Chris Zwiryk
quelle
Tolle Erweiterung, aber könnten Sie eine Dokumentation hinzufügen? Was ist die beabsichtigte Verwendung des zurückgegebenen Wertes? Warum ist Index var eher als spezifisch ein int? Beispielnutzung?
Mark T
Markieren: Der Rückgabewert ist die Anzahl der Elemente in der Liste. Index wird dank impliziter Typisierung speziell als int eingegeben.
Chris Zwiryk
@Chris, basierend auf Ihrem obigen Code, ist der Rückgabewert der auf Null basierende Index des letzten Werts in der Liste, nicht die Anzahl der Elemente in der Liste.
Eric Smith
@Chris, nein, ist es nicht: Der Index wird erhöht, nachdem der Wert übernommen wurde (Postfix-Inkrement). Nachdem das erste Element verarbeitet wurde, ist der Index 1 und so weiter. Der Rückgabewert ist also wirklich die Elementanzahl.
MartinStettner
1
@MartinStettner Der Kommentar von Eric Smith am 16.5. Stammt aus einer früheren Version. Er korrigierte den Code am 18.5., Um den korrekten Wert zurückzugeben.
Michael Blackburn
16

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.

Jay Bazuzi
quelle
1
a) Dies ist nicht einmal eine Antwort auf die Frage. b) Diese Antwort wäre klarer, wenn im Vorfeld erwähnt würde, dass es zwar keine Enumerable.ForEach<T>Methode gibt, aber eine List<T>.ForEachMethode. 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); }
Jim Balter
13

Ich habe mich immer gefragt, warum ich das immer bei mir habe:

public static void ForEach<T>(this IEnumerable<T> col, Action<T> action)
{
    if (action == null)
    {
        throw new ArgumentNullException("action");
    }
    foreach (var item in col)
    {
        action(item);
    }
}

Schöne kleine Verlängerungsmethode.

Aaron Powell
quelle
2
col könnte null sein ... das kannst du auch überprüfen.
Amy B
Ja, ich checke in meiner Bibliothek ein, ich habe es nur verpasst, als ich es dort schnell gemacht habe.
Aaron Powell
Ich finde es interessant, dass jeder den Aktionsparameter auf null überprüft, aber niemals den Parameter source / col.
Cameron MacFarland
2
Ich finde es interessant, dass jeder Parameter auf null überprüft, wenn er nicht auch Ergebnisse in einer Ausnahme überprüft ...
oɔɯǝɹ
Ganz und gar nicht. Eine Null Ref-Ausnahme würde Ihnen nicht den Namen des fehlerhaften Parameters mitteilen. Und Sie können auch in der Schleife einchecken, um einen bestimmten Null-Ref zu werfen, der besagt, dass col [index #] aus demselben Grund null ist
Roger Willcocks
8

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:

collection.Where(i => i.Name = "hello").Select(i => i.FullName);

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.

Scott Dorman
quelle
7

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).

IEnumerable<string> people = new List<string>(){"alica", "bob", "john", "pete"};
people.Select(p => { Console.WriteLine(p); return p; });

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:

static IEnumerable<T> WithLazySideEffect(this IEnumerable<T> src, Action<T> action) {
  return src.Select(i => { action(i); return i; } );
}

Der obige Code wird dann people.WithLazySideEffect(p => Console.WriteLine(p))effektiv zu foreach, aber faul und verkettbar.

Martijn
quelle
Fantastisch, es hat nie geklickt, dass eine zurückgegebene Auswahl so funktionieren würde. Eine nette Verwendung der Methodensyntax, da ich nicht glaube, dass Sie dies mit der Ausdruckssyntax tun könnten (daher habe ich mir das noch nie so vorgestellt).
Alex KeySmith
@AlexKeySmith Sie könnten dies mit der Ausdruckssyntax tun, wenn C # einen Sequenzoperator wie seinen Vorfahren hätte. Der springende Punkt von ForEach ist jedoch, dass eine Aktion mit dem Typ void ausgeführt wird und Sie keine void-Typen in Ausdrücken in C # verwenden können.
Jim Balter
Imho, jetzt Selectmacht 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.
Matthias Burger
@MatthiasBurger Wenn Selectnicht das getan wird, was es tun soll, ist dies ein Problem bei der Implementierung von Select, nicht bei dem aufrufenden Code Select, der keinen Einfluss darauf hat, was zu tun Selectist.
Martijn
4

@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 ersetzt

Action<blah,blah> f = { foo };

List1.ForEach(p => f(p))
List2.ForEach(p => f(p))

usw...

Die Logik befindet sich an einem Ort und Sie haben Ihre Klasse nicht verschmutzt.

Spoulson
quelle
foreach (var p in List1.Concat (List2) .Concat (List3)) {... mache Sachen ...}
Cameron MacFarland
Wenn es so einfach ist wie f(p), lassen Sie die { }Kurzform weg : for (var p in List1.Concat(List2).Concat(List2)) f(p);.
Spoulson
3

Die meisten LINQ-Erweiterungsmethoden geben Ergebnisse zurück. ForEach passt nicht in dieses Muster, da es nichts zurückgibt.

Leppie
quelle
2
ForEach verwendet Action <T>, eine andere Form des Delegierten
Aaron Powell,
2
Mit Ausnahme von Leppies Recht geben alle Enumerable-Erweiterungsmethoden einen Wert zurück, sodass ForEach nicht zum Muster passt. Die Delegierten kommen darauf nicht ein.
Cameron MacFarland
Nur dass eine Erweiterungsmethode kein Ergebnis zurückgeben muss, dient sie dazu, eine Möglichkeit hinzuzufügen, eine häufig verwendete Operation aufzurufen
Aaron Powell,
1
In der Tat, Slace. Was ist, wenn kein Wert zurückgegeben wird?
TraumaPony
1
@ Martinij iepublic IEnumerable<T> Foreach<T>(this IEnumerable<T> items, Action<T> action) { /* error check */ foreach (var item in items) { action(item); yield return item; } }
McKay
3

Wenn Sie F # haben (das in der nächsten Version von .NET enthalten sein wird), können Sie verwenden

Seq.iter doSomething myIEnumerable

TraumaPony
quelle
9
Microsoft hat sich daher entschieden, keine ForEach-Methode für eine IEnumerable in C # und VB bereitzustellen, da diese nicht rein funktional wäre. dennoch haben sie es (name iter) in ihrer funktionaleren Sprache F # bereitgestellt. Ihre Logik spielt auf mich an.
Scott Hutchinson
3

Teilweise liegt es daran, dass die Sprachdesigner aus philosophischer Sicht nicht damit einverstanden sind.

  • Keine Funktion zu haben (und zu testen ...) ist weniger Arbeit als eine Funktion zu haben.
  • Es ist nicht wirklich kürzer (es gibt einige Fälle, in denen es vorübergehende Funktionen gibt, aber das wäre nicht die primäre Verwendung).
  • Es ist das Ziel, Nebenwirkungen zu haben, worum es bei linq nicht geht.
  • Warum haben wir eine andere Möglichkeit, dasselbe zu tun wie eine Funktion, die wir bereits haben? (foreach Schlüsselwort)

https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/

McKay
quelle
2

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.

Paco
quelle
a) Dies ist keine Antwort auf die Frage. b) ToList erfordert zusätzliche Zeit und Speicher.
Jim Balter
2

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

Kirill Osenkov
quelle
Wurde dies jemals umgesetzt? Ich stelle mir das nicht vor, aber gibt es eine andere URL, die dieses Ticket ersetzt? MS Connect wurde eingestellt
knocte
Dies wurde nicht implementiert.
Erwägen
2

Bin ich es oder ist die Liste <T> .Foreach wurde von Linq so ziemlich obsolet gemacht. Ursprünglich gab es

foreach(X x in Y) 

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

IEnumerator<int> enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
    int i = enumerator.Current;

    Console.WriteLine(i);
}

(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/

user18784
quelle
1
Ich glaube, der generierte Code für "foreach" sollte einen Try-finally-Block haben. Weil der IEnumerator von T von der Schnittstelle IDisposable erbt. Im generierten finally-Block sollte der IEnumerator der T-Instanz also entsorgt werden.
Morgan Cheng
2

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:

private static IEnumerable<T> ForEach<T>(IEnumerable<T> xs, Action<T> f) {
    foreach (var x in xs) {
        f(x); yield return x;
    }
}
Fredefox
quelle
1
Dies ist das gleiche wieSelect(x => { f(x); return x;})
Jim Balter
0

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.

Eichhörnchen
quelle
5
Ist das wirklich der Fall? Ich kann nicht erkennen, wie foreachviel weniger Kompilierungszeit überprüft wird. Sicher, wenn Ihre Sammlung eine nicht generische IEnumerable ist, ist die Schleifenvariable eine object, aber das Gleiche gilt für eine hypothetische ForEachErweiterungsmethode.
Richard Poole
1
Dies wird in der akzeptierten Antwort erwähnt. Es ist jedoch einfach falsch (und Richard Poole oben ist richtig).
Jim Balter