Trattare con zuppa di parentesi graffe ricci


11

Ho programmato in C # e VB.NET per anni, ma principalmente in VB. Sto facendo uno spostamento verso C # e, nel complesso, mi piace C # meglio.

Un problema che sto riscontrando, tuttavia, è la zuppa di parentesi graffe. In VB, ogni parola chiave della struttura ha una parola chiave di chiusura corrispondente, ad esempio:

Namespace ...
    Class ...
        Function ...
            For ...
                Using ...
                    If ...
                        ...
                    End If
                    If ...
                        ...
                    End If
                End Using
            Next
        End Function
    End Class
End Namespace

Lo stesso codice scritto in C # finisce per essere molto difficile da leggere:

namespace ... {
    class ... {
        function ... {
            for ... {
                using ... {
                    if ... {
                        ...
                    }
                    if ... {
                        ...
                    }
                }
            }
            // wait... what level is this?
        }
    }
}

Essendo così abituato a VB, mi chiedo se esiste una tecnica impiegata dai programmatori c-style per migliorare la leggibilità e per garantire che il codice finisca nel "blocco" corretto. L'esempio sopra è relativamente facile da leggere, ma a volte alla fine di un pezzo di codice avrò 8 o più livelli di parentesi graffe, che richiedono di scorrere diverse pagine per capire quale parentesi finisce il blocco che mi interessa in.


83
So che questo può sembrare un predicatore, e forse hai condizioni speciali che lo richiedono (perché sì, a volte è necessario - per fortuna questi tempi dovrebbero essere rari), ma di solito "... 8 o più livelli di parentesi graffe, che mi richiedono di scorrere su diverse pagine per capire a quale parentesi graffa termina il blocco che mi interessa " significa che il codice ha bisogno di un serio refactoring e cleanup.
FrustratedWithFormsDesigner

7
Una cosa che ho visto fatto, e che ho iniziato a fare, è alla fine di una parentesi graffa, aggiungerò un commento a riguardo. Qualcosa del genere // End's using X statement.
PiousVenom,

14
@FrustratedWithFormsDesigner è perfetto sul fatto che il tuo flusso di controllo è un disastro e deve essere rifatto. Detto questo, penso che ti lamenti più delle parentesi graffe che dello scoping e direi che devi solo abituarti. Imparare una lingua con una sintassi significativamente diversa da quella a cui sei abituato sembra sicuramente un po 'di zuppa per un po', ma con la pratica che va via. Hai solo bisogno di piegarti e affrontare fino a quando il tuo cervello inizia a elaborare la sintassi in modo più naturale, ci arriverai.
Jimmy Hoffa,

2
@FrustratedWithFormsDesigner - Devo pulire molti oggetti COM in una situazione di interoperabilità COM. Sto usando la tecnica suggerita in questo articolo: jake.ginnivan.net/vsto-com-interop . Questo crea facilmente due o tre strati. Quando si sovrappongono il ciclo for, la funzione, la classe e lo spazio dei nomi (insieme a un'istruzione if) si arriva facilmente a diversi livelli di parentesi graffe.
JDB ricorda ancora Monica il

5
@TyrionLannister - E quelli possono essere alcuni dei commenti più veloci per non essere sincronizzati con ciò a cui appartengono ... Penso che se avessi qualcosa del genere, preferirei che si generasse automaticamente (in mostra -time solo, non persistente) dall'IDE.
Clockwork-Muse

Risposte:


39

Metti la parentesi graffa iniziale nello stesso "rango" di quella finale, in questo modo:

namespace ... 
{
    class ... 
    {
        function ... 
        {
            for ... 
            {
                using ... 
                {
                    if ... 
                    {
                        ...
                    }
                    if ... 
                    {
                        ...
                    }
                }
            }
            // It's the `function` level!
        }
    }
}

15
Sono d'accordo. Le parentesi egiziane mi fanno venire il mal di testa.
PiousVenom,

8
Inoltre, la maggior parte degli IDE (probabilmente) evidenzia il partner di un tutore quando fai clic su di esso.
StuperUser

3
@TyrionLannister: Grazie per avermi finalmente dato un termine per quello stile! Non ho mai avuto un buon nome per loro se non "parentesi errate".
FrustratedWithFormsDesigner

3
@ Cyborgx37: il tuo IDE ha una funzione "Vai alla parentesi graffa corrispondente"? Solitamente associato a una scorciatoia da tastiera che sposta automaticamente il cursore sulla parentesi che corrisponde a quella attualmente evidenziata.
FrustratedWithFormsDesigner

3
Devo dire che non vedo come questo renda più facile. In ogni caso, basta guardare lo schermo a livello di rientro della parentesi graffa fino ad arrivare a una parola chiave. E con tutte quelle linee extra, ora devi cercare.
Blorgbeard esce il

14
  • A seconda dell'IDE: posiziona il cursore sulla parentesi aperta / chiusa per evidenziare sia la parentesi che la corrispondente.
  • Comprimi il blocco e ti mostra dove si apre / chiude.
  • Scrivi blocchi di codice più piccoli. Sul serio. Dai un'occhiata Clean Codee non incontrare mai più questo problema (e avere un codice più leggibile / gestibile).

Una nota, la seguente è una sintassi c # valida che potrebbe aiutare la tua situazione particolare:

using (var type = new MyDisposable1())
using (var type2 = new MyDisposable2())
{
    /* do what you will with type2 and type2 */
}

La caccia al singolo personaggio evidenziato è ciò che mi ha spinto a pubblicare questa domanda in primo luogo.
JDB ricorda ancora Monica il

2
@ Cyborgx37: Da qui il punto 3. Se l'intero blocco di codice si adatta allo schermo, non devi mai cacciare. Nella maggior parte / in tutte le classi che scrivo, le uniche coppie di parentesi graffe che non rientrano nello schermo sono namespace / class.
Steven Evers,

Quello che suggerisci ai punti 1 e 2 è quello che faccio ora ... ma questo richiede che io lasci il posto in cui sto programmando e inizi a manipolare la mia visione del codice per capire dove mettere la riga successiva. Il punto 3 è ben preso, ma non sempre possibile, soprattutto se il tuo codice richiede diversi strati di usingblocchi (vedi jake.ginnivan.net/vsto-com-interop )
JDB ricorda ancora Monica

@ Cyborgx37: vedi la mia modifica.
Steven Evers,

1
@ Cyborgx37 Se scegli un colore che si distingue da tutto il resto (ho usato lo sfondo viola e il testo bianco per un po ', IIRC), allora non c'è bisogno di cacciare il tutore corrispondente - praticamente ti grida "Sono QUI ! ".
un CVn

5

Una convenzione comune è aggiungere un commento dopo la parentesi graffa di chiusura per indicare la struttura che sta chiudendo:

if {
   ...
} // end if

while (condition) {
   ...
} // end while

ecc. Non mi sono mai riscaldato a questa convenzione da solo, ma alcune persone lo trovano utile.


16
Ho visto persone fare questo, e quando scambiano / cambiano l'inizio del blocco (cambiano a whilein for, scambiano le ifdichiarazioni) quasi non si ricordano MAI di aggiornare i commenti di chiusura, rendendoli peggiori che inutili. Questa convenzione sarà probabilmente utile solo se puoi costringerti a mantenere i commenti ogni volta che {cambia la natura della corrispondenza .
FrustratedWithFormsDesigner

Sì, ho fatto un po 'di questo, ma è un sacco di lavoro extra (e rumore). Speravo che ci sarebbe stato qualcosa di più semplice.
JDB ricorda ancora Monica il

5
Le osservazioni devono solo spiegare perché mai cosa o come come fa il codice sia di quelli e basandosi su commenti per spiegare una di esse significa che il codice è difficile da leggere che è un segno il codice deve essere corretto, non ha commentato.
Jimmy Hoffa,

1
Questo mi fa domandare perché nessuno abbia scritto un editor che mostri questi commenti, ma che in realtà non li includa nel codice.
Brendan Long,

2
@JimmyHoffa: penso che una regola migliore per i commenti sia che dovrebbero fornire chiarezza . Di solito questo significa rispondere "perché", ma può anche significare altre cose. Non rimanere così preso dal dogma che ti impedisce di fare cose che realmente aiutano, come l'occasionale aggiunta di un commento a una parentesi graffa di chiusura che è molto lontana dalla parentesi aperta.
Bryan Oakley,

5

In generale, quando diventa difficile abbinare le parentesi graffe in qualsiasi stile, probabilmente significa che il metodo è troppo lungo e dovrebbe essere riconsiderato.


5

Penso che devi rinforzarlo con le parentesi graffe. Alla fine diventeranno una seconda natura per te e ti starai chiedendo come hai mai vissuto senza di loro.

Assicurati però che siano rientrati in modo appropriato e che venga seguita una convenzione di spaziatura (non importa quale).


Un anno dopo, e questo consiglio suona vero. :)
JDB ricorda ancora Monica il

4

Rimuovo 2 livelli di annidamento comprimendo lo spazio dei nomi e gli ambiti delle classi in senso orizzontale. Si noti che i metodi sono allineati con il bordo sinistro dello schermo. Non vedo il punto di perdere 2 livelli di rientro in ogni file.

Dopodiché è raro che tu abbia mai annidato più di 4 livelli di profondità.

namespace FooNameSpace {
class Foo {

public void bar()
{
    while(true)
    {
        while(true)
        {
            break;
        }
    }
}

public void fooBar()
{
    foreach(var item in FooList)
    {
        foreach(var b in item.Bars)
        {
            if(b.IsReady)
            {
                bar();
            }
            bar();
        }
        bar();
    }
}

}}//end class, namespace

Mi piace questa idea, ma Visual Studio non sembra supportarla (almeno, 2008. Stiamo aggiornando al 2012 entro la fine dell'anno, quindi spero)
JDB ricorda ancora Monica

@ Cyborgx37. Modifico il testo esternamente in VIM, quindi non è un problema. Ma in Visual Studio fai: Control + A, quindi premi il pulsante "indent less". Fallo solo per i nuovi file. Non preoccuparti dei file esistenti in quanto confonderà i confronti diff nel controllo del codice sorgente.
mike30,

Digitando parentesi quadre / parentesi graffe avvia una formattazione automatica di VS ma puoi sempre premere CTRL + z per rifiutare il suo suggerimento.
Alex A Parigi,

1

Di recente ho deciso di provare a formalizzare due regole sui costrutti di controllo del flusso che sostanzialmente vanno così:

  • Non dovresti avere altro che costrutti di flusso di codice necessari
  • Dovresti rendere i costrutti del flusso di codice il più piccoli possibile

Proprio per i motivi che hai citato e di cui sei chiaramente consapevole, penso che queste siano ottime regole da seguire. Ci sono un paio di semplici tecniche che puoi utilizzare per realizzarle:

  • Esci dall'ambito appena possibile (questo include l'ambito dei loop e le funzioni)
  • Fai attenzione agli altri che potrebbero essere mitigati uscendo dalla funzione dal precedente if e applica la tecnica di uscire dall'ambito come ho già detto
  • Invertire i controlli condizionali quando il codice all'interno di un if è eccezionale rispetto a quello esterno
  • Codice fattore all'interno di un loop verso un altro metodo quando la dimensione del loop aumenta per oscurare il resto del metodo
  • Controlla eventuali ambiti che contengono solo un altro ambito, ad esempio una funzione il cui intero ambito è riempito da un if con niente al di fuori dell'if

Ho dettagliato qui esempi di come non seguirli può finire con te facendo come hai detto e mettendo il codice nel blocco di codice errato, il che è male e una facile causa di bug che si presentano durante la manutenzione.


Grazie, potrebbero esserci un blocco o due che potrei rifatturare. Aiuterà, ma è difficile rimodellare diversi usingblocchi nidificati .
JDB ricorda ancora Monica il

@ Cyborgx37 attualmente i blocchi possono essere integrati se sono ambiti soddisfacenti nidificati, guarda qui stackoverflow.com/questions/1329739/… funziona fondamentalmente come il controllo del flusso a linea singola costruisce come puoi se (true) doSomething (); puoi anche if (true) if (somethingElse) if (otherThings) {doThis (); Fai quello(); fare qualsiasi cosa(); } e gli if nidificano come ti aspetti (NON SCRIVERE IL CODICE COME QUELLO PER L'AMORE DI DIO, FATTO SOLO CON GLI USI E NIENTE ALTRO heh)
Jimmy Hoffa,

Sì, so di annidare usando i blocchi, ma questo funziona solo se hai una sola riga sotto. Nella maggior parte del mio codice, non è così. Ancora un post molto utile nel complesso ...
ho

@ Cyborgx37 sì, mi rendo conto che l'annidamento dell'oscilloscopio funziona solo se non hai bit extra, detto che c'è un modello nel modo in cui usi? Sarebbe forse possibile spostare parte di quel extra nel costruttore se è nella parte superiore o lo smaltimento se è nella parte inferiore? Questo è se è modellato; indovinando potrebbe essere da quando hai sollevato questo problema, quindi probabilmente ti imbatti in questi nidi spesso nel tuo codice.
Jimmy Hoffa,

In realtà ho iniziato a lavorare su un'implementazione del modello Factory che avvolge gli oggetti COM in una classe "AutoCleanup". Quindi uso una singola usingistruzione sulla classe factory e, quando viene eliminata, elimina automaticamente tutte le classi concluse. Finora funziona bene e ha notevolmente ridotto il numero di usingistruzioni nel mio codice.
JDB ricorda ancora Monica l'

1

Questa è purtroppo una delle più antiche cause di guerra nell'informatica. Argomenti ragionevoli possono essere fatti da entrambe le parti (migliore economia immobiliare verticale rispetto a una più facile capacità di abbinare visivamente parentesi aperta con parentesi chiusa), ma in realtà un semplice formattatore di codice sorgente risolverà tutto per te. MS Visual C # ne ha uno integrato che funziona bene.

Tuttavia, tieni presente che se stai lavorando come parte di una squadra, ti dovrai aspettare di conformarti alle convenzioni utilizzate da quella squadra, quindi vale la pena acquisire familiarità con entrambi gli stili e astenersi dal diventare religiosi rispetto agli stili di parentesi.

Quindi, mentre stai imparando con tutti i mezzi, concentrati sullo stile che ti rende più facile da imparare, ma tieni d'occhio l'altro mentre ci sei e farai bene.


1
Non sono sicuro che si tratti del formattatore predefinito o qualcosa del genere in ReSharper, ma quando usavo C # avevamo un set di opzioni che riformattava il codice come parte del check-in. In questo modo è possibile formattare il codice come desiderato mentre si stava lavorando con esso, ma sarebbe stato riformattato in "standard di progetto" al momento del check-in. Penso che l'unico vero standard di formattazione che abbiamo avuto sia stato l'uso di spazi anziché di schede.
TMN,

1

Usa Resharper, che ti aiuterà a raccomandare modi per ridurre l'annidamento. Inoltre, leggi il libro Clean Code di Bob Martin , che sottolinea che una funzione dovrebbe fare solo una cosa, e quindi ogni funzione dovrebbe essere lunga solo una mezza dozzina di righe, quindi non avrai molti livelli di annidamento di cui preoccuparti.


1

C'è un componente aggiuntivo nell'editor che può aiutarti in: C # Outline .

Il componente aggiuntivo estende l'editor VS20xx per C # aggiungendo funzionalità per comprimere, espandere ed evidenziare blocchi di codice nidificati. Queste funzionalità consentono di modificare e leggere più facilmente i contenuti nidificati di blocchi di codice come if, while, ecc.


0

Se scrivi il tuo codice in Visual Studio, c'è anche un plugin che mostra punti verticali tra l'inizio e la fine di ogni struttura che costruisci.

Ma nel complesso penso che ci vorrà solo un po 'di tempo prima che tu sia abituato al "Curly-Braces-Soup". (A proposito, mi piace molto quell'espressione. Sembra un po 'un episodio-nome per The Big Bang Theory)


0

Il rientro ti dice dove sei, in entrambi gli stili di sintassi. Se scrivi un programma VB o un programma C # su una sola riga, presto non sarai in grado di dire dove ti trovi nella sintassi nidificata. La macchina analizza le frasi finali del blocco o le parentesi graffe, ma gli umani hanno bisogno di rientranze.

Le frasi di fine blocco provengono da un'era di schede perforate e nastro di carta, quando la programmazione era molto meno interattiva e visiva. O, davvero, per nulla interattivo. È stato difficile accedere ai programmi, quindi i programmatori avevano bisogno di compilatori per essere molto intelligenti nell'analisi della sintassi e nel recupero degli errori.

In quell'epoca passata, il ciclo di esecuzione della compilazione-compilazione potrebbe aver comportato la preparazione di schede perforate con un perforatore di carte, e quindi l'allineamento a una finestra di presentazione del lavoro in cui un impiegato prendeva le schede perforate e le inviava alla macchina. Più tardi, il programmatore avrebbe raccolto l'output (stampato su carta) da un'altra finestra. Se il programma avesse errori, l'output consisterebbe solo nella diagnostica del compilatore. Quando i tempi di risposta sono lunghi, il costo aggiuntivo della digitazione end ifanziché solo )è giustificato se aiuta a migliorare la qualità della diagnostica, poiché il programmatore deve correggere il maggior numero possibile di errori in un'unica iterazione per ridurre il numero di perdite di tempo iterazioni attraverso la finestra di invio del lavoro.

Quando manca una parentesi graffa di chiusura, è difficile dire quale parentesi aperta è quella che non è chiusa. (Il compilatore potrebbe dover analizzare il rientro per fare un'ipotesi istruita.) Se si elimina una parentesi graffa di chiusura all'interno di una funzione, sembra che l'intero resto del file faccia parte di quella funzione, risultando in una raffica di messaggi di errore inutili. Considerando che se si dispone di una end functionsintassi, il compilatore può dedurre dove termina la funzione errata, ripristinare e analizzare correttamente le funzioni successive, fornendo eventuali diagnosi aggiuntive che sono significative.

Quando lavori in un editor di testo sensibile al codice che rientra e colora automaticamente il tuo codice, su uno schermo ad alta risoluzione in cui puoi vedere sessanta o più righe, gli argomenti per quei tipi di linguaggi goffi non si applicano più. Puoi modificare e ricostruire in modo incrementale i programmi così velocemente da poter gestire solo un errore alla volta. Inoltre, vedendo grandi sezioni del programma contemporaneamente sullo schermo e mantenendo il rientro corretto, è possibile ridurre in primo luogo il verificarsi di questi tipi di errori di annidamento. E un buon editor di testi di programmazione segnalerà anche alcuni tipi di errori di sintassi durante la digitazione. Inoltre, ci sono editor pieghevoli che faranno crollare i blocchi di un programma in base alla sua sintassi, dando una visione "simile a uno schema" della sua struttura.

Lisp ha usato le parentesi sin dall'inizio e forse, non a caso, gli hacker di Lisp sono stati i pionieri della programmazione come esperienza interattiva costruendo sistemi che accettavano programmi in piccoli pezzi (espressioni).

In effetti, non hai bisogno di simboli finali, come dimostra il linguaggio Python. L'identificazione può essere solo la struttura. Gli umani usano già il rientro per individuare la struttura del codice anche nelle lingue in cui la macchina si basa sulla fine di simboli o frasi.


-1

Se stai usando un IDE, premi Crtl+ k+ De l'IDE farà il resto del lavoro.


quale eclisse IDE usa ctrl-shift-i per il rientro e ctrl-shift-f per la formattazione
maniaco del cricchetto

check in visual studio
Jagz W
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.