2009-07-23 9 views
3

Die generische List-Klasse hat eine .ForEach(Action<T> action) Methode. Jetzt habe ich einige einfache Timings von, wie sie beide durchführen, und es scheint, dass das generische ForEach der ärmere Darsteller ist. Der (Snippet Compiler Friendly) Code ist unter -.Net - Wann ist die Liste <T> .ForEach über eine Standard-foreach-Schleife bevorzugt?

public static class timer{ 
    public static long foreachloop = 0; 
    public static long Gforeachloop = 0;} 

public class something{ 
    public List<string> myStrings = new List<string>(); 

    public something() 
    { 
     for(int i = 1; i<=5000000;i++) 
     { 
      myStrings.Add(i.ToString()); 
     } 
    }} 

public class cls1{ 
    private static List<string> Strings = new List<string>(); 
    private static List<string> OtherStrings = new List<string>(); 

    public static void RunSnippet() 
    { 
     something s = new something(); 

     Stopwatch watch = new Stopwatch(); 
     watch.Start(); 
     foreach(string x in s.myStrings) 
     { 
      Strings.Add(x); 
     } 
     watch.Stop(); 
     timer.foreachloop = watch.ElapsedMilliseconds; 

     watch.Reset(); 
     watch.Start(); 

     s.myStrings.ForEach(delegate(string n){OtherStrings.Add(n);}); 

     s.myStrings.Clear(); 

     watch.Stop(); 
     timer.Gforeachloop = watch.ElapsedMilliseconds; 

     WL("FOREACH-"+timer.foreachloop + ",Count = " + Strings.Count); 
     WL("GFOREACH-"+timer.Gforeachloop + ",Count = " + OtherStrings.Count); 
    } 

    #region Helper methods 

    public static void Main() 
    { 
     try 
     { 
      RunSnippet(); 
     } 
     catch (Exception e) 
     { 
      string error = string.Format("---\nThe following error occurred while executing the snippet:\n{0}\n---", e.ToString()); 
      Console.WriteLine(error); 
     } 
     finally 
     { 
      Console.Write("Press any key to continue..."); 
      Console.ReadKey(); 
     } 
    } 

    private static void WL(object text, params object[] args) 
    { 
     Console.WriteLine(text.ToString(), args); 
    } 

    private static void RL() 
    { 
     Console.ReadLine(); 
    } 

    private static void Break() 
    { 
     System.Diagnostics.Debugger.Break(); 
    } 

    #endregion 
} 

FOREACH kommt bei 177ms heraus und GFOREACH bei 707ms.

Jetzt vermute ich, es gibt einen guten Grund dafür, es zu benutzen, aber ich kann nur an einen nicht denken. Natürlich ist Leistung nicht der Grund, also ist die Frage, wann wäre es die beste Option?

Vielen Dank im Voraus.

+1

Verwandte/Dupe? http://stackoverflow.com/questions/1172472/when-would-i-use-listt-foreach-over-a-native-foreach-loop – Brandon

Antwort

7

Dieser Blog-Eintrag von Eric Lippert gibt den Hintergrund:

http://blogs.msdn.com/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx

Er über den gemeinsamen Vorschlag einer Verlängerung Methode sprach die gleiche Sache für IEnumerable<T>, aber die philosophische Einwand gilt für auch zu tun .

Das deutet darauf hin, dass diese Methode vielleicht nie so eine gute Idee war, obwohl sie "cool" aussieht. Es ist einfacher, einfach foreach zu verwenden.

Ich habe vorgeschlagen, dass solche Methoden als a fix for the classic closure-over-loop-variable bug gedacht werden können.

Aber in der Praxis bin ich gerade besser darin gewesen, solche Bugs zu entdecken.

+0

Er spricht hauptsächlich über '.ForEach' on 'IEnumerable ' (wo Sie ketten und komponieren Zeug), nicht auf "Liste ", wo Sie eine Methode für jedes Objekt in der Liste in einer einzigen Zeile ausführen möchten. –

+0

Aber es gibt keinen weit verbreiteten Sinn, in dem "ForEach" auf "IEnumerable " zusammensetzbar wäre. Die offensichtliche, konsistente Implementierung besteht darin, 'List .ForEach' nachzuahmen und void zurückzugeben, genau wie Eric es in seinem Blogpostbeispiel getan hat. Und so gelten für beide die gleichen philosophischen Einwände. –

+0

Und in Bezug auf das Loop-Variable-Closure-Problem, ich wünschte nur, dass sie anonyme Delegaten und Lambdas "schlau" über Loop-Variablen gemacht haben, eine Kopie von ihnen standardmäßig erfassen. In den extrem seltenen Fällen, in denen der Codierer beabsichtigt, die Schleifenvariable von innerhalb des Lambda zu modifizieren, könnten sie um ihn codieren. Die anderen 99% der Zeit hätten wir ein leichteres Leben gehabt. Eines der Dinge, die jetzt sehr schwer zu beheben wären, würde ich mir vorstellen. –

6

Wenn es besser aussieht.

Kein Witz überhaupt. Wirklich, ich meine es ernst. Gehen Sie mit dem besser lesbaren Stil in Ihrem Fall. Zum Beispiel, wenn Sie sich wie nur anrufen für jedes Element eine Methode wollen:

list.ForEach(Console.WriteLine); 

dieser Stil passt besser. Wenn Sie jedoch 100 Zeilen als Schleifenkörper haben oder verschachtelte Schleifen und Flusskonstrukte steuern, sieht der alte Stil besser aus.

+0

sieht einfach komisch zu mir. Ich würde den Standard foreach viel einfacher visuell zu konsumieren finden. – AnthonyWJones

+0

AnthonyWJones: Ich denke, das liegt vor allem daran, dass wir an die imperative Programmierung gewöhnt sind. Dieser Stil sieht für Programmierer, die funktionale Sprachen verwenden, definitiv natürlicher aus. –

+0

@Mehrdad - nicht wirklich. Sieh dir Haskell an. Beim Schreiben von imperativem Code mit Nebeneffekten (worum es sich handelt) verwenden sie eine spezielle Monad-Syntax, die den Stil der Imperativsprachen nachahmt. –

Verwandte Themen