2015-07-14 19 views
5

Nachdem ich kürzlich während der Programmierung auf dieses Problem gestoßen bin, habe ich mich darüber gewundert. Unten sind 2 Snippets, die sowohl legal als auch kompilierbar sind. Genauer gesagt, meine Frage ist dies ... im zweiten Fall, machen die Klammern das Programm langsamer? Auch warum ist das erlaubt?Reduzieren unnötige geschweifte Klammern die Leistung?

1. Fall:

if (statement) 
{ 
// do something 
} 

2. Fall:

{ 
    if (statement) 
    { 
     // do something 
    } 
} 

Zusätzlich was, wenn ich unter so etwas wie der Code hatte .. Ist die Laufzeit die gleiche wie gerade Aufruf Funktion X ohne die geschweiften Klammern.

{ 
    { 
    { 
     // call function X 
    } 
    } 
} 
+2

Nein, sie werden nur zum Gruppieren verwendet. Auch diese Frage ist hier nicht Thema. – deathismyfriend

+3

Technisch sind sie Bereiche, aber ich würde annehmen, dass der Compiler intelligent genug ist, um die unnötigen Bereiche zu entfernen. – MiltoxBeyond

+1

Hinzufügen von zusätzlichen geschweiften Klammern in unwahrscheinlich, das Programm langsamer zu machen, da sie zum Angeben des Bereichs verwendet werden. Diese Frage wurde [hier] diskutiert (http://stackoverflow.com/questions/6136853/why-does-c-sharp-allow-code-blocks-without-a-preceding-statement). –

Antwort

11

Die meisten der Zeit macht es keinen Unterschied - und Sie sollten Code für mehr Lesbarkeit als alles andere auf jeden Fall.

Allerdings können geschweifte Klammern auf eine überraschende Weise einen Effekt auf die Leistung haben, obwohl es ziemlich ungewöhnlich ist. Betrachten Sie diesen Code:

using System; 
using System.Collections.Generic; 

class Test 
{ 
    static void FewerCurlies() 
    { 
     List<Action> actions = new List<Action>(); 
     for (int i = 0; i < 100; i++) 
     { 
      int x; 
      if (i % 3 == 0) 
      { 
       actions.Add(() => x = 10); 
      } 

      int y; 
      if (i % 3 == 1) 
      { 
       actions.Add(() => y = 10); 
      } 
     } 
    } 

    static void MoreCurlies() 
    { 
     List<Action> actions = new List<Action>(); 
     for (int i = 0; i < 100; i++) 
     { 
      { 
       int x; 
       if (i % 3 == 0) 
       { 
        actions.Add(() => x = 10); 
       } 
      } 

      { 
       int y; 
       if (i % 3 == 1) 
       { 
        actions.Add(() => y = 10); 
       } 
      } 
     } 
    } 
} 

Die zusätzlichen Verstrebungen in MoreCurlies Blick überflüssig, nicht wahr? Nicht ganz ... der generierte Code sieht mehr wie folgt aus:

using System; 
using System.Collections.Generic; 

class Test 
{ 
    static void FewerCurlies() 
    { 
     List<Action> actions = new List<Action>(); 
     for (int i = 0; i < 100; i++) 
     { 
      FewerCurliesCapture capture = new FewerCurliesCapture(); 
      if (i % 3 == 0) 
      { 
       actions.Add(capture.Method1); 
      } 

      if (i % 3 == 1) 
      { 
       actions.Add(capture.Method2); 
      } 
     } 
    } 

    static void MoreCurlies() 
    { 
     List<Action> actions = new List<Action>(); 
     for (int i = 0; i < 100; i++) 
     { 
      { 
       MoreCurliesCapture1 capture = new MoreCurliesCapture1(); 
       if (i % 3 == 0) 
       { 
        actions.Add(capture.Method); 
       } 
      } 

      { 
       MoreCurliesCapture1 capture = new MoreCurliesCapture2(); 
       if (i % 3 == 1) 
       { 
        actions.Add(capture.Method); 
       } 
      } 
     } 
    } 

    private class FewerCurliesCapture 
    { 
     public int x; 
     public int y; 

     public void Method1() 
     { 
      x = 10; 
     } 

     public void Method2() 
     { 
      y = 10; 
     } 
    } 

    private class MoreCurliesCapture1 
    { 
     public int x; 

     public void Method() 
     { 
      x = 10; 
     } 
    } 

    private class MoreCurliesCapture2 
    { 
     public int y; 

     public void Method() 
     { 
      y = 10; 
     } 
    } 
} 

Die Unterschiede sind hier:

  • Eine Instanz der Capture-Klasse wird in jeder Iteration der Schleife erstellt in FewerCurlies, auch wenn es ist nicht
  • jede Instanz der Capture-Klasse in FewerCurlies enthält beide Variablen, obwohl jeder Delegierte eine von ihnen verwenden nur tatsächlich verwendet, während in MoreCurlies jeder Capture-Klasse erfasst nur eine einzelne Variable

Dies ist alles etwas umsetzungsspezifisch, aber es zeigt, dass redundant aussehende Curlies einen Einfluss haben können.

+0

Was ist die Logik, die Capture-Variable in den For-Block in der 'WenigerCurliesCapture' zu ​​setzen? Warum nicht im 'if' Block? Wie kommt es, dass der Compiler intelligent genug ist, um zu sehen, dass es ausreicht, eine Capture-Klasse im 'FewerCurlies'-Beispiel zu verwenden, aber nicht im 'MoreCurlies'-Beispiel? –

+0

@ SimonAndréForsberg: Du könntest es tatsächlich in das 'if' schreiben - ich habe gerade den Unterschied demonstriert, den der Umfang machen kann. Ich würde argumentieren, dass der Compiler im Fall "MoreCurlies" schlauer war - in diesem Fall sind zwei kleinere Capture-Klassen besser als eine große.Im Grunde genommen hat der Compiler jedoch eine Erfassungsklasse pro Bereich, die alle erfassten Variablen aus diesem Bereich erfasst. Zumindest ist dies die aktuelle Implementierung ... –

+0

Für weitere Diskussionen von Jon Skeet und Eric Lippert, siehe [SO: Diskrete anonyme Methoden teilen eine Klasse?] (Http://stackoverflow.com/q/3885106/18192) – Brian

1

Keine geschweiften Klammern, die die Leistung nicht reduzieren.

Etwas ist hilfreich, Code zu verstehen und gute Formatierung von Code bereitzustellen. Aber einige geschweifte Klammern sind obligatorisch wie Funktion Start/Ende, Schleife Start/Ende, Bedingung Start/Ende und diese Balken auch helfen, shrop der Variablen zu verstehen.

3

Kurze Antwort ist "Nein, sie reduzieren nicht die Leistung".

Lockige Klammern werden benötigt, damit der Compiler den Gültigkeitsbereich der Variablen bestimmen und wissen kann, wo die aktuelle Gruppe von Anweisungen endet. Sobald der Compiler die Verarbeitung beendet hat, würde der Code mit und ohne die unnötigen geschweiften Klammern identische Ausgabe erzeugen.

Beachten Sie, dass dies auf die Leistung von kompiliertem Code bezieht, nicht auf die Leistung des Compilers selbst. Der Compiler benötigt zusätzliche Zeit, um den Code zu kompilieren, einfach weil die Rohgröße der Eingabe größer ist. Damit diese zusätzliche Zeit messbar wird, muss die Anzahl unnötiger Klammern jedoch extrem sein.

+1

"Sobald der Compiler die Verarbeitung beendet hat, würde der Code mit und ohne die unnötigen geschweiften Klammern identische Ausgabe erzeugen." Es wäre in diesem Fall. In einigen Fällen kann es jedoch in Bezug auf erfasste Variablen einen Unterschied machen. –

+0

@JonSkeet, ich bin mir nicht sicher, ob das stimmt. Bitte sehen Sie meine Antwort. Ich habe versucht, die Variable in einen anderen Bereich zu setzen, wo sie modifiziert und dann zurückgegeben wurde, aber die IL war immer noch identisch. –

+0

@DavidArno: Es ist * wahr, aber nur, wenn Sie Variablen erfassen, was Ihr Code nicht tut ... und selbst dann würde es genau darauf ankommen, was Sie tun. –

-1

Es verursacht keine Leistungseinbußen, aber die Verwendung von geschweiften Klammern erhöht definitiv die Lesbarkeit Ihres Codes. Im realen Wortszenario, wenn Sie Peer-Code-Bewertungen oder Paar-Programmierung haben, dann wären die von Ihnen geschriebenen Dinge viel klarer und lesbarer.

Gehen Sie über den folgenden Link

http://www.c-sharpcorner.com/UploadFile/d0e913/lame-question-of-the-day-role-of-curly-braces-in-our-cod/

+0

Jeder Grund für downVote. ..? – Anamay

4

Wie immer bei solchen Fragen, die Antwort liegt in den IL es generiert. Für die folgenden Codebeispiele:

public int X() 
{ 
    { 
     { 
      { 
       return 0; 
      } 
     } 
    } 
} 

public int Y() 
{ 
    return 0; 
} 

wir mit folgenden kompilierten IL am Ende:

.method public hidebysig instance int32 X() cil managed 
{ 
    // Code size  2 (0x2) 
    .maxstack 8 
    IL_0000: ldc.i4.0 
    IL_0001: ret 
} // end of method SomeType::X 

.method public hidebysig instance int32 Y() cil managed 
{ 
    // Code size  2 (0x2) 
    .maxstack 8 
    IL_0000: ldc.i4.0 
    IL_0001: ret 
} // end of method SomeType::Y 

Sie sind identisch. also nein, es hat keinen Einfluss auf die Leistung. X ist schrecklich zu lesen, aber das ist ein anderes Problem.

aktualisieren {} wirkt sich auf den Umfang der Variablen, so vielleicht könnte dies eine Wirkung haben.Wieder ist überprüfen lassen:

public int X() 
{ 
    var i = 1; 
    { 
     { 
      i++; 
      { 
       return i; 
      } 
     } 
    } 
} 

public int Y() 
{ 
    var i = 1; 
    i++; 
    return i; 
} 

Sobald jedoch wieder, ist die IL produziert identisch: Jedoch

// Code size  8 (0x8) 
.maxstack 2 
.locals init ([0] int32 i) 
IL_0000: ldc.i4.1 
IL_0001: stloc.0 
IL_0002: ldloc.0 
IL_0003: ldc.i4.1 
IL_0004: add 
IL_0005: stloc.0 
IL_0006: ldloc.0 
IL_0007: ret 

, wenn die Variable in einem Verschluss erfasst wird, macht es die Dinge beeinflussen. X im folgenden Fall nicht mehr IL schaffen, die einen Einfluss auf die Leistung haben würde:

public Func<int> X() 
{ 
    { 
     var i = 1; 
     { 
      i++; 
      { 
       return() => i; 
      } 
     } 
    } 
} 

public Func<int> Y() 
{ 
    var i = 1; 
    i++; 
    return() => i; 
} 
+0

In .NET 4.5 wird der gleiche IL-Code für die Methoden 'X' und' Y' vom letzten Snippet erzeugt. – Kapol

2

Im Gegensatz zu C++, in der der Compiler Code zu generieren verlangt werden, wenn Variablen in oder außerhalb des Bereichs gehen, die meisten Variablen in C# werden effektiv in den umschließenden Funktionsebenenumfang gehisst. Der Code:

void foo() 
{ 
    { 
    int i; 
    ... stuff using i as int 
    } 
    { 
    char i; 
    ... stuff using i as char 
    } 
} 

effektiv verwandelte sich in bekommen:

void foo() 
{ 
    int i__1; 
    char i__2; 
    ... stuff using i__1 as int 
    ... stuff using i__2 as char 
} 

mit Code aus dem ersten verspannten Abschnitt die erste Variable i__1 wo immer es i verwendet haben würde, und der Code in die zweite mit i__2 . In einigen Fällen ist es möglich, dass die Deklaration von Variablen mit demselben Namen und demselben Zweck in mehreren Scoping-Blöcken weniger effizienten Code generiert als die Deklaration einer Variablen mit diesem gemeinsamen Zweck in einem äußeren Scoping-Block, aber nur selten eine sinnvolle Wirkung hat. In den meisten Fällen kann der Just-in-Time-Compiler feststellen, dass mehrere Variablen im Code sicher auf denselben Speicherort abgebildet werden können, und selbst in solchen, in denen der für einige zusätzliche Variablen erforderliche Speicher nicht möglich ist die Leistung sehr beeinflussen.

0

unnötige geschweiften Klammern, vorausgesetzt, Sie keine verschachtelten Variablen haben, fügt nur ein Etikett auf dem Etikettentabelle während, während es Ihren Code-Byte-Code oder Maschinencode ist zu konvertieren. Im schlimmsten Fall wird die Bauzeit langsamer sein.Wenn Sie Variablen in der Verschachtelung haben, sollten Sie immer noch keine Probleme haben, wenn sie Primitive sind, die keinen Destruktionscode haben, aber wenn Sie Objekte innerhalb der geschachtelten geschweiften Klammern haben, wäre ein besseres Verständnis der GC erforderlich, aber ich bezweifle stark dass irgendein wahrnehmbarer Unterschied vorhanden wäre. In allen Fällen, weil der Compiler zusätzliche Arbeit leistet, indem er Referenzen in einer Nachschlagetabelle speichert, während er Ihr Projekt aufbaut, wird es eine, wenn auch wahrscheinlich nicht wahrnehmbare, leichte Verzögerung bei der Erstellung Ihres Projekts geben.

Verwandte Themen