196 Stimmen

Foreach vs someList.ForEach(){}

Es gibt anscheinend viele Möglichkeiten, über eine Sammlung zu iterieren. Ich frage mich, ob es Unterschiede gibt oder warum man eine Methode der anderen vorziehen würde.

Erster Typ:

List someList = 
foreach(string s in someList) {

}

Andere Möglichkeit:

List someList = 
someList.ForEach(delegate(string s) {

});

Ich nehme an, dass anstatt des anonymen Delegaten, den ich oben benutze, einen wieder verwendbaren Delegaten verwenden könnten...

14voto

Daniel Earwicker Punkte 111630

Ich kenne zwei obskure Dinge, die sie unterscheiden. Los geht's!

Erstens gibt es den klassischen Fehler, für jedes Element in der Liste einen Delegaten zu erstellen. Wenn Sie das foreach-Schlüsselwort verwenden, können alle Ihre Delegaten am Ende auf das letzte Element der Liste verweisen:

    // Eine Liste von Aktionen, die später ausgeführt werden sollen
    List actions = new List();

    // Zahlen von 0 bis 9
    List numbers = Enumerable.Range(0, 10).ToList();

    // Speichern einer Aktion, die jede Zahl druckt (FALSCH!)
    foreach (int number in numbers)
        actions.Add(() => Console.WriteLine(number));

    // Führen Sie die Aktionen aus, wir drucken tatsächlich 10 Kopien von "9"
    foreach (Action action in actions)
        action();

    // Also versuchen wir es erneut
    actions.Clear();

    // Speichern einer Aktion, die jede Zahl druckt (RICHTIG!)
    numbers.ForEach(number =>
        actions.Add(() => Console.WriteLine(number)));

    // Führen Sie die Aktionen aus
    foreach (Action action in actions)
        action();

Die List.ForEach-Methode hat dieses Problem nicht. Das aktuelle Element der Iteration wird an die äußere Lambda als Argument übergeben, und dann erfasst die innere Lambda dieses Argument korrekt in ihrem eigenen Closure. Problem gelöst.

(Leider glaube ich, dass ForEach ein Element von List ist, anstatt eine Erweiterungsmethode zu sein, obwohl es einfach ist, sie selbst zu definieren, damit Sie über diesen Mechanismus auf jeden Enumerable-Typ zugreifen können.)

Zweitens hat der Ansatz der ForEach-Methode eine Einschränkung. Wenn Sie IEnumerable implementieren, indem Sie yield return verwenden, können Sie kein yield return innerhalb der Lambda durchführen. Das Durchlaufen der Elemente in einer Sammlung, um Dinge mittels yield return zurückzugeben, ist auf diese Weise nicht möglich. Sie müssen das foreach-Schlüsselwort verwenden und das Closure-Problem umgehen, indem Sie manuell eine Kopie des aktuellen Schleifenwerts innerhalb der Schleife erstellen.

Hier mehr

6voto

Craig.Nicol Punkte 1784

Sie könnten dem anonymen Delegaten einen Namen geben :-)

Und Sie können das zweite wie folgt schreiben:

someList.ForEach(s => s.ToUpper())

Was ich bevorzuge und eine Menge Schreibarbeit spart.

Wie Joachim sagt, ist Parallelität leichter auf die zweite Form anzuwenden.

5voto

Joel Coehoorn Punkte 377088

List.ForEach() wird als funktionaler angesehen.

List.ForEach() sagt, was du möchtest. foreach(item in list) sagt auch genau, wie du es möchtest. Das lässt List.ForEach frei, die Implementierung des wie-Teils in Zukunft zu ändern. Zum Beispiel könnte eine hypothetische zukünftige Version von .Net immer davon ausgehen, dass List.ForEach parallel ausgeführt wird, unter der Annahme, dass zu diesem Zeitpunkt jeder eine Reihe von CPU-Kernen hat, die im Allgemeinen ungenutzt sind.

Andererseits gibt dir foreach(item in list) etwas mehr Kontrolle über die Schleife. Du weißt zum Beispiel, dass die Elemente in irgendeiner Art von sequenzieller Reihenfolge durchlaufen werden und du könntest leicht in der Mitte abbrechen, wenn ein Element eine bestimmte Bedingung erfüllt.


Weitere aktuelle Bemerkungen zu diesem Thema finden Sie hier:

https://stackoverflow.com/a/529197/3043

2voto

jezell Punkte 2522

Hinter den Kulissen wird der anonyme Delegat in eine tatsächliche Methode umgewandelt, sodass möglicherweise Overhead bei der zweiten Wahl entstehen könnte, wenn der Compiler sich nicht dafür entscheidet, die Funktion zu inline. Darüber hinaus würden lokale Variablen, auf die der Körper des anonymen Delegaten Bezug nimmt, aufgrund von Compilertricks in ihrer Natur verändert, um zu verbergen, dass sie zu einer neuen Methode kompiliert werden. Weitere Informationen dazu, wie C# dieses Zaubertrick vollführt, finden Sie hier:

http://blogs.msdn.com/oldnewthing/archive/2006/08/04/688527.aspx

2voto

Pablo Caballero Punkte 336

Die ForEach-Funktion ist ein Element der generischen Klasse List.

Ich habe die folgende Erweiterung erstellt, um den internen Code zu reproduzieren:

public static class MyExtension
    {
        public static void MyForEach(this IEnumerable collection, Action action)
        {
            foreach (T item in collection)
                action.Invoke(item);
        }
    }

Am Ende verwenden wir also ein normales foreach (oder eine Schleife, wenn gewünscht).

Andererseits ist die Verwendung einer Delegatenfunktion nur eine andere Möglichkeit, eine Funktion zu definieren. Dieser Code:

delegate(string s) {

}

ist äquivalent zu:

private static void myFunction(string s, )
{

}

oder unter Verwendung von Lambda-Ausdrücken:

(s) =>

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X