2015-07-14 6 views
5

Dopo averlo incontrato di recente durante la programmazione, mi sono chiesto questo. Di seguito sono riportati 2 snippet che sono sia legali che compilabili. Nello specifico, la mia domanda è questa .. nel secondo caso, le parentesi rendono il programma più lento? Inoltre, perché è permesso?Le parentesi graffe non necessarie riducono le prestazioni?

1 ° caso:

if (statement) 
{ 
// do something 
} 

2 ° caso:

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

Inoltre quello che se avessi avuto qualcosa come il codice qui sotto .. è il runtime lo stesso come solo chiamando la funzione X senza alcuna parentesi graffa.

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

No sono solo utilizzati per il raggruppamento. Anche questa domanda è fuori tema qui. – deathismyfriend

+3

Tecnicamente sono ambiti ma suppongo che il compilatore sia abbastanza intelligente da rimuovere gli ambiti non necessari. – MiltoxBeyond

+1

È improbabile aggiungere ulteriori parentesi graffe per rallentare il programma, poiché vengono utilizzati per specificare l'ambito. Questa domanda è stata discussa [qui] (http://stackoverflow.com/questions/6136853/why-does-c-sharp-allow-code-blocks-without-a-preceding-statement). –

risposta

11

La maggior parte di del tempo non fa alcuna differenza - e si dovrebbe sicuramente codificare per la leggibilità più di ogni altra cosa.

Tuttavia, le parentesi graffe possono hanno un effetto sulle prestazioni, in un modo sorprendente, anche se è piuttosto insolito. Considerate questo codice:

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); 
       } 
      } 
     } 
    } 
} 

Le parentesi supplementari in MoreCurlies sguardo ridondante, giusto? Non proprio ... il codice generato assomiglia più a questo:

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; 
     } 
    } 
} 

Le differenze qui sono:

  • Un'istanza della classe di cattura viene creato in ogni iterazione del ciclo in FewerCurlies, anche se non è usato
  • ogni istanza della classe cattura utilizzato in FewerCurlies contiene entrambe le variabili, anche se ogni delegato sarà solo effettivamente utilizzare uno di loro, mentre in ogni classe MoreCurlies cattura cattura solo una singola variabile

Questo è tutto un po 'specifico per l'implementazione, ma mostra che gli indigeni ridondanti possono avere un impatto pari a.

+0

Qual è la logica di inserire la variabile di cattura nel blocco predefinito in 'FewerCurliesCapture'? Perché non all'interno del blocco 'if'? Com'è che il compilatore è abbastanza intelligente da vedere che è sufficiente usare una classe di acquisizione nell'esempio 'FewerCurlies', ma non nell'esempio' MoreCurlies'? –

+0

@ SimonAndréForsberg: Si potrebbe effettivamente inserire nel 'if' - stavo solo dimostrando la differenza che lo scope può fare. Direi che il compilatore era più intelligente nel caso di 'MoreCurlies' - avendo due classi di acquisizione più piccole è meglio di una grande, in questo caso.Ma fondamentalmente il compilatore termina con una classe di acquisizione per ambito, che cattura tutte le variabili catturate da quell'ambito. Almeno, questo è l'attuale implementazione ... –

+0

Per ulteriori discussioni di Jon Skeet e Eric Lippert, vedi [SO: metodi discreti anonimi che condividono una classe?] (Http://stackoverflow.com/q/3885106/18192) – Brian

1

Nessuna parentesi graffa non riduce le prestazioni.

Qualcosa è utile per capire il codice e fornire una buona formattazione del codice. Ma alcune parentesi graffe sono obbligatorie come la funzione inizio/fine, ciclo inizio/fine, condizione inizio/fine e anche queste barce aiutano a comprendere lo scrop delle variabili.

3

Risposta breve è "no, non riducono le prestazioni".

Le parentesi graffe sono necessarie al compilatore per determinare l'ambito delle variabili e per sapere dove finisce il gruppo di istruzioni corrente. Una volta che il compilatore ha terminato l'elaborazione, il codice con e senza le parentesi graffe non necessarie produrrebbe un output identico.

Si noti che questo è correlato alle prestazioni del codice compilato, non alle prestazioni del compilatore stesso. Il compilatore impiegherà più tempo per compilare il codice, semplicemente perché la dimensione non elaborata dell'input è maggiore. Tuttavia, affinché questo tempo aggiuntivo diventi misurabile, il numero di parentesi non necessarie deve essere piuttosto estremo.

+1

"Una volta che il compilatore ha terminato l'elaborazione, il codice con e senza le parentesi graffe non necessarie produrrebbe un risultato identico." Sarebbe in questo caso. In alcuni casi, tuttavia, può fare la differenza in termini di variabili acquisite. –

+0

@JonSkeet, non sono sicuro che sia vero. Per favore vedi la mia risposta. Ho provato a inserire la variabile in un ambito diverso in cui è stata modificata e quindi restituita, ma l'IL era ancora identico. –

+0

@DavidArno: È * è * vero, ma solo quando si acquisiscono variabili, che il proprio codice non ... e anche in quel caso dipenderebbe esattamente da ciò che si sta facendo. –

-1

Non causa alcuna riduzione delle prestazioni, ma l'utilizzo di parentesi graffe aumenta sicuramente la leggibilità del codice. Nello scenario delle parole reali quando si hanno recensioni di peer code o paia di programmazione, le cose scritte da voi sarebbero molto chiare e leggibili.

passare attraverso il seguente link

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

+0

Qualsiasi motivo per downVote. ..? – Anamay

4

Come mai con queste domande, la risposta sta nella L'esso genera. Per i seguenti esempi di codice:

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

public int Y() 
{ 
    return 0; 
} 

si finisce con il seguente: IL compilato

.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 

Sono identici. quindi no, non ha alcun effetto sulle prestazioni. X è orribile da leggere, ma questo è un altro problema.

Aggiornamento {} incida sulla portata delle variabili, quindi forse questo potrebbe avere un effetto.Ancora una volta, controlliamo:

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

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

una volta, però, l'IL prodotto è identico:

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

Tuttavia, se la variabile è catturato in una chiusura, essa incide cose. X nel seguente caso vuol creare più di IL, che avrebbe un impatto sulle prestazioni:

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 viene prodotto lo stesso codice IL per i metodi 'X' e' Y' dell'ultimo snippet. – Kapol

2

A differenza di C++, in cui il compilatore può essere richiesto per generare il codice quando le variabili vanno dentro o fuori del campo di applicazione, la maggior parte delle variabili I C# vengono effettivamente sollevati per includere l'ambito del livello di funzione. Il codice:

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

sarà effettivamente arrivare trasformato in:

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

con codice dalla prima sezione Braced usando la prima variabile i__1 dovunque avrebbe utilizzato i, e il codice nella seconda utilizzando i__2 . In alcuni casi è possibile che la dichiarazione di variabili con lo stesso nome e lo stesso scopo all'interno di più blocchi di scope possa generare un codice meno efficiente rispetto alla dichiarazione di una variabile con tale scopo comune in un blocco di scope esterno, ma raramente avrà un effetto significativo. Nella maggior parte dei casi il compilatore just-in-time è in grado di determinare che più variabili nel codice possono essere mappate in modo sicuro nella stessa posizione di archiviazione e anche in quelle in cui non è possibile che lo spazio di archiviazione richiesto per alcune variabili extra sia improbabile influenzare molto le prestazioni.

0

L'uso di parentesi graffe non necessarie, presupponendo che non ci siano variabili nidificate, aggiunge solo un'etichetta alla tabella delle etichette durante la conversione del codice in byte-code o codice macchina. Quindi in peggio il tempo di costruzione sarà più lento.Se hai delle variabili nel nesting, non dovresti avere problemi se sono primitive che non hanno il codice di distruzione, ma se hai oggetti creati all'interno delle parentesi graffe allora sarebbe necessaria una maggiore comprensione del GC, ma dubito fortemente che qualsiasi differenza apprezzabile sarebbe presente. In tutti i casi, poiché il compilatore sta facendo un lavoro extra memorizzando i riferimenti in una tabella di ricerca durante la creazione del progetto, ci sarà un leggero ritardo nella creazione del progetto, anche se probabilmente è impercettibile.