2010-07-27 6 views
17

Syntaktisch sehe ich, dass sie endlos Schleife, bis eine break-Anweisung erreicht wird, aber sind sie auf die gleiche Sache kompiliert? Ist das für etwas schneller, weil es keine Bedingung zu überprüfen hat? Gibt es neben der Lesbarkeit des Codes sogar einen Unterschied?Unterschied zwischen for (;;) und while (true) in C#?

+0

Siehe http://stackoverflow.com/questions/2288856/when-implementing-an-infinite-loop-is-there-a-difference-in-using-while1-vs-fo –

+0

Dies ist eine gute Frage. Ich finde es interessant zu sehen, dass der Compiler den Code bis zu einer do while-Schleife im Beispielcode optimiert, mit dem ich das versucht habe. –

+1

'für (;;)' ist A) geschrieben von ehemaligen C-Codern und B) vier Zeichen kürzer :) – hobbs

Antwort

36

Vor diesem Eingang:

private static void ForLoop() 
{ 
    int n = 0; 
    for (; ;) 
    { 
     Console.WriteLine(n++); 
    } 
} 

private static void WhileLoop() 
{ 
    int n = 0; 
    while (true) 
    { 
     Console.WriteLine(n++); 
    } 
} 

... Sie erhalten diese Ausgabe:

.method private hidebysig static void ForLoop() cil managed 
{ 
    // Code size  14 (0xe) 
    .maxstack 3 
    .locals init ([0] int32 n) 
    IL_0000: ldc.i4.0 
    IL_0001: stloc.0 
    IL_0002: ldloc.0 
    IL_0003: dup 
    IL_0004: ldc.i4.1 
    IL_0005: add 
    IL_0006: stloc.0 
    IL_0007: call  void [mscorlib]System.Console::WriteLine(int32) 
    IL_000c: br.s  IL_0002 
} // end of method Program::ForLoop 


.method private hidebysig static void WhileLoop() cil managed 
{ 
    // Code size  14 (0xe) 
    .maxstack 3 
    .locals init ([0] int32 n) 
    IL_0000: ldc.i4.0 
    IL_0001: stloc.0 
    IL_0002: ldloc.0 
    IL_0003: dup 
    IL_0004: ldc.i4.1 
    IL_0005: add 
    IL_0006: stloc.0 
    IL_0007: call  void [mscorlib]System.Console::WriteLine(int32) 
    IL_000c: br.s  IL_0002 
} // end of method Program::WhileLoop 

täuschend ähnlich, würde ich sagen (identisch, auch).

+0

Wenn ich etwas nicht vermisse, sind sie * identisch *, außerhalb der Kommentare und Bezeichnernamen. –

+0

@ T.E.D. Ja, sie sind identisch. –

+18

Hmm, in Zeile 5 gibt es einen Unterschied ... oh, warte, nein, das ist ein Fleck auf meinem Monitor;) –

2

Ich habe den Ausgangscode nicht untersucht, aber es sollte überhaupt keinen Unterschied geben. Jeder ordentliche Compiler wird einfach genug Schleifenoptimierung durchführen, um zu sehen, dass die Bedingung ein konstanter Ausdruck ist, und muss daher nicht jede Iteration überprüfen.

Wenn man schneller als die anderen ist, müssen die C# Compiler Schriftsteller etwas ‚splained ihnen ...

21

In modernen Compiler, absolut nichts.

Historisch wurde jedoch for(;;) als ein einziger Sprung implementiert, während while(true) auch eine Überprüfung für wahr hatte.

Ich bevorzuge while(true), da es klarer macht, was ich tue.

+2

+1 absolut. 'while (true)' ist viel klarer als 'for (;;)'. Worauf warte ich eigentlich? –

+1

Ich stimme Ihnen herzlich zu, dass Sie das auswählen sollten, von dem Sie denken, dass es klarer ist, obwohl ich vielleicht nicht zustimmen könnte, welches ... –

+2

warum nicht 'loop: ... goto loop'? –

0

Sie kompilieren zu der gleichen Sache. Sie können eine Test-App schreiben, die beide Methoden implementiert und anschließend ILDASM verwendet, um zu bestätigen, dass IL identisch ist.

+3

Oder Sie können Fredriks Antwort einfach anschauen und sich die Mühe ersparen. :) – DarLom

0

Ein Debug-Assembly erfüllt bis zu (wahr). Verwenden Sie Reflektor und Sie können die Ergebnisse sehen.

static void Main(string[] args) 
    { 
     ExecuteWhile(); 

     ExecuteFor(); 
    } 

    private static void ExecuteFor() 
    { 
     for (; ;) 
     { 
      Console.WriteLine("for"); 
      string val = Console.ReadLine(); 
      if (string.IsNullOrEmpty(val)) 
      { 
       Console.WriteLine("Exit for."); 
       break; 
      } 
     } 
    } 

    private static void ExecuteWhile() 
    { 
     while (true) 
     { 
      Console.WriteLine("while"); 
      string val = Console.ReadLine(); 
      if (string.IsNullOrEmpty(val)) 
      { 
       Console.WriteLine("Exit while."); 
       break; 
      } 
     } 
    } 

Inspizieren des ExecuteFor Verfahren Reflektor.

private static void ExecuteFor() 
{ 
    while (true) 
    { 
     Console.WriteLine("for"); 
     if (string.IsNullOrEmpty(Console.ReadLine())) 
     { 
      Console.WriteLine("Exit for."); 
      return; 
     } 
    } 
} 

Eine optimierte Version des gleichen Code erzeugt unterschiedliche Ergebnisse für ExecuteFor

private static void ExecuteFor() 
{ 
    do 
    { 
     Console.WriteLine("for"); 
    } 
    while (!string.IsNullOrEmpty(Console.ReadLine())); 
    Console.WriteLine("Exit for."); 
} 

Für Ausführlichkeit hier ist die optimierte ExecuteWhile ...

private static void ExecuteWhile() 
{ 
    do 
    { 
     Console.WriteLine("while"); 
    } 
    while (!string.IsNullOrEmpty(Console.ReadLine())); 
    Console.WriteLine("Exit while."); 
} 
+0

Kompiliert es wirklich auf 'while (true)', oder reflektor reflector es so? : p –

+0

Wenn Sie sich die IL anschauen, die aus den gleichen Baugruppen erstellt wurde, liest es sich so. –

0

Wie bereits von anderen erwähnt, mit irgend moderner Compiler, sollte es absolut keinen Unterschied machen.

Ich habe einen kurzen Test auf meinem Computer gemacht, ein paar Operationen mit dem einen oder dem anderen Timing, und sie haben immer ungefähr die gleiche Zeit genommen. Natürlich sind diese Tests wegen anderer laufender Prozesse nicht 100% genau, aber wenn es einen Geschwindigkeitsunterschied gibt (sollte es nicht sein), dann ist es ein mikroskopischer.

1

Wenn ich könnte, würde ich vorschlagen, dass Sie eine etwas andere Frage betrachten. Wenn Sie beide häufig genug zur Pflege verwenden, strukturieren Sie Ihren Code wahrscheinlich schlecht. Zwar gibt es Dinge wie eingebettete Systeme, die wirklich für immer laufen, Schleifen in normalem Code jedoch nicht. Das Schreiben einer Schleife, die Ansprüche für immer läuft, bedeutet normalerweise, dass Sie die Ausgangsbedingung für die Schleife irgendwo innerhalb mit einem anderen Kontrollfluss (z., if (whatever) break;) als real Ausfahrt aus der Schleife.

Das kann und sollte normalerweise vermieden werden. Während es Situationen gibt, in denen break Aussagen sinnvoll sind, sollten sie im Allgemeinen ungewöhnliche Situationen behandeln, nicht zum Schreiben einer Schleife, die eine Sache sagt, aber eine andere (dh "läuft für immer"), aber wirklich "läuft, bis die Bedingung erfüllt ist".).

+0

Dem kann ich nicht zustimmen. –

+0

Ich auch nicht. Als ich zum ersten Mal lernte, in PL/I zu programmieren (ich weiß nicht, ob es überhaupt eine 'Break'-Anweisung hatte), war das Muster "Zeile lesen", während es nicht der End-Marker ist, mache die Schleife, die endet mit dem Lesen der nächsten Zeile ". Das scheint eine Verletzung von "wiederhole dich nicht". Ich würde vorschlagen, dass das Lesen der Zeile einmal am Anfang der Schleife und das Beenden, wenn es eine Endmarkierung ist, ein besserer Stil ist. – supercat

+0

@supercat: Ich stimme zu, aber es rechtfertigt nichts. Sie sollten 'while (file.getline())', nicht 'für (;;) if (! File.getline()) break;' –

Verwandte Themen