Perché C # consente {} blocchi di codice senza un'istruzione precedente?


86

Perché C # permette blocchi di codice senza una dichiarazione precedente (ad esempio if, else, for, while)?

void Main()
{
    {   // any sense in this?
        Console.Write("foo");
    }
}

70
C'è qualche motivo per cui non dovrebbe ?
Jamiec

30
Ma come dimostrano le risposte, ha più significato di "non fa male, quindi permettetelo". Questo è il punto di porre domande del genere.
Seldon

8
+1 domanda suona abbastanza innocente, ma le risposte mi hanno davvero insegnato qualcosa di prezioso
Nicolas78

7
@ Akash: Senza chiedere perché, non miglioreremo mai.
richard

7
@ Akash: Sembra un atteggiamento elitario di alto livello. Le persone hanno sempre domande, e se non c'è motivo di chiedere perché, allora non ha senso avere SO. Questo sito non è tanto per far sì che le persone risolvano i nostri problemi immediati (anche se ciò accade), ma per fornire un archivio di domande e risposte che ci aiuteranno a essere tutti programmatori migliori. COSÌ è per chiedere PERCHÉ! :-)
richard

Risposte:


90

Nel contesto che dai, non c'è significato. Scrivere una stringa costante sulla console funzionerà allo stesso modo ovunque nel flusso del programma. 1

Invece, in genere li usi per limitare l'ambito di alcune variabili locali. Questo è ulteriormente elaborato qui e qui . Guardate la risposta di João Angelo e la risposta di Chris Wallis per gli esempi brevi. Credo che lo stesso si applichi anche ad altri linguaggi con sintassi in stile C, non che siano rilevanti per questa domanda.


1 A meno che, naturalmente, non decidi di provare a essere divertente e di creare la tua Consolelezione, con un Write()metodo che fa qualcosa di completamente inaspettato.


24
Ri: altre lingue: di solito, ma non sempre. JavaScript è un'eccezione notevole; la dichiarazione di una variabile locale in un particolare blocco non definisce l'ambito della variabile nel blocco.
Eric Lippert

@EricLippert: In C #, la dichiarazione di una variabile all'interno di un blocco causa l'ambito della variabile dal punto di vista del runtime? Da quello che posso dire, le durate delle variabili spesso non sono limitate da blocchi di scoping, un fatto che è osservabile in progetti a linguaggio misto se la prima cosa che si fa con una variabile in un ciclo è passarla come outparametro a un'implementazione dell'interfaccia scritta in un'altra lingua e tale implementazione legge la variabile prima di scriverla.
supercat

E in C ++, a causa di RAII invece di GC, è molto più utile.
Deduplicator

In C ++, si comporta in modo molto simile a un blocco try / catch, quindi le variabili e le classi dichiarate all'interno di quello scope vengono fuori dallo stack quando lasci quell'ambito. Questo aiuta a prevenire le collisioni di nomi e riduce l'impronta di memoria della funzione. Rimuove anche quelle variabili dal completamento automatico dell'editor di codice. Spesso trovo i blocchi try / catch più utili.
Kit10

144

La { ... }presenta almeno l'effetto collaterale di introdurre un nuovo ambito per le variabili locali.

Tendo a usarli nelle switchistruzioni per fornire un ambito diverso per ogni caso e in questo modo mi consente di definire la variabile locale con lo stesso nome nella posizione più vicina possibile del loro utilizzo e per denotare anche che sono valide solo a livello di caso.



6
Se hai bisogno di ottiche nei tuoi casi, sono troppo grandi! Metodo di estrazione.
Jay Bazuzi

20
@ Jay Bazuzi, solo perché voglio avere una variabile locale con lo stesso nome in più di un caso non significa che debbano essere enormi. Stai solo saltando a conclusioni enormi ... :)
João Angelo

Congratulazioni per il tuo badge Ottima risposta! :)
BoltClock

1
@BoltClock, grazie, ho solo bisogno di qualche altra domanda {}per mantenere quei badge d'oro in arrivo ... :)
João Angelo

56

Non è tanto una funzionalità di C # quanto un effetto collaterale logico di molti linguaggi di sintassi C che utilizzano le parentesi graffe per definire l'ambito .

Nel tuo esempio le parentesi graffe non hanno alcun effetto, ma nel codice seguente definiscono l'ambito, e quindi la visibilità, di una variabile:

Ciò è consentito poiché esco dall'ambito nel primo blocco e viene definito di nuovo nel successivo:

{
    {
        int i = 0;
    }

    {
        int i = 0;
    }
}

Ciò non è consentito in quanto i è uscito dall'ambito e non è più visibile nell'ambito esterno:

{
    {
        int i = 0;
    }

    i = 1;
}

E così via.


1
Ho letto delle disparità nella nomenclatura delle parentesi in vari modi di inglese, ma in quale sapore sono {}conosciuti come parentesi?
BoltClock

Buona domanda! Immagino che un mio mentore me l'abbia piantato nel cervello dieci anni fa. Probabilmente dovrei chiamarli "parentesi graffe" o "parentesi graffe". Ognuno per conto proprio ... en.wikipedia.org/wiki/…
Chris Wallis

10
Nel mio vocabolario "(" = parentesi, "{" = parentesi graffa, "[" = parentesi quadre
pb.

Aspetta, li chiamerei parentesi graffe e Wikipedia include quella denominazione. L'Oxford English Dictionary definisce questo uso delle parentesi come segue: One of two marks of the form [ ] or ( ), and in mathematical use also {}, used for enclosing a word or number of words, a portion of a mathematical formula, or the like, so as to separate it from the context; In ogni caso non sono parentesi, ma "parentesi graffa" sembra OK.
Dumbledad

IME, "parentesi graffe" e "parentesi quadre".
cp.engr

18

Considero {}un'affermazione che può contenere diverse affermazioni.

Si consideri un if che esiste fuori di un'espressione booleana seguita da una dichiarazione. Questo funzionerebbe:

if (true) Console.Write("FooBar");

Funzionerebbe anche:

if (true)
{
  Console.Write("Foo");
  Console.Write("Bar");
}

Se non sbaglio, questa viene chiamata istruzione di blocco.

Poiché {}può contenere altre affermazioni, ne può contenere anche altre {}. L'ambito di una variabile è definito dal suo genitore {}(istruzione block).

Il punto che sto cercando di sottolineare è che {}è solo un'affermazione, quindi non richiede un se o altro ...


+1 Questo è esattamente ciò che sono le parentesi graffe nei linguaggi in stile C - una cosiddetta "istruzione composta".
Michael Ekstrand

12

La regola generale nei linguaggi con sintassi C è "qualsiasi cosa tra { }dovrebbe essere trattata come una singola istruzione e può andare ovunque una singola istruzione potrebbe":

  • Dopo un if.
  • Dopo un for, whileo do.
  • Ovunque nel codice .

A tutti gli effetti, è perché la grammatica della lingua includeva questo:

     <statement> :== <definition of valid statement> | "{" <statement-list> "}"
<statement-list> :== <statement> | <statement-list> <statement>

Cioè, "un'affermazione può essere composta da (varie cose) o da una parentesi graffa di apertura, seguita da un elenco di istruzioni (che può includere una o più affermazioni), seguita da una parentesi graffa chiusa". IE "un { }blocco può sostituire qualsiasi istruzione, ovunque". Compreso nel mezzo del codice.

Non consentire un { }blocco ovunque possa andare una singola istruzione avrebbe effettivamente reso la definizione del linguaggio più complessa .


Qualcuno ha appena aumentato questa risposta (dopo 9 anni), che in realtà non era una risposta specifica a questa domanda, ma molto più una discussione generica. Potrebbe benissimo essere stato sostituito dalla lingua e dalle biblioteche .. Ma è stato comunque un piacere, grazie.
Massimo

1

Perché C ++ (e java) consentivano blocchi di codice senza un'istruzione precedente.

C ++ li ha consentiti perché C lo ha fatto.

Si potrebbe dire che tutto si riduce al fatto che il design del linguaggio di programma USA (basato su C) ha vinto piuttosto che il design del linguaggio di programma europeo ( basato su Modula-2 ).

(Le istruzioni di controllo agiscono su una singola istruzione, le istruzioni possono essere gruppi per creare nuove istruzioni)


Intendi Module2 o Modula2?
Richard Ev,

In effetti, questa è la risposta corretta. Inoltre, risponderei semplicemente "Perché ogni linguaggio informatico lo fa".
Fattie


0

1 Perché ... è mantenere l'area dell'ambito dell'istruzione .. o funzione, questo è davvero utile per il codice grande ..

{
    {
        // Here this 'i' is we can use for this scope only and out side of this scope we can't get this 'i' variable.

        int i = 0;
    }

    {
        int i = 0;
    }
}

inserisci qui la descrizione dell'immagine


0

Hai chiesto "perché" C # consente blocchi di codice senza istruzioni precedenti. La domanda "perché" potrebbe anche essere interpretata come "quali sarebbero i possibili vantaggi di questo costrutto?"

Personalmente, utilizzo blocchi di codice senza istruzioni in C # in cui la leggibilità è notevolmente migliorata per altri sviluppatori, tenendo presente che il blocco di codice limita l'ambito delle variabili locali. Ad esempio, considera il seguente frammento di codice, che è molto più facile da leggere grazie ai blocchi di codice aggiuntivi:

OrgUnit world = new OrgUnit() { Name = "World" };
{
    OrgUnit europe = new OrgUnit() { Name = "Europe" };
    world.SubUnits.Add(europe);
    {
        OrgUnit germany = new OrgUnit() { Name = "Germany" };
        europe.SubUnits.Add(germany);

        //...etc.
    }
}
//...commit structure to DB here

Sono consapevole che questo potrebbe essere risolto in modo più elegante utilizzando metodi per ogni livello di struttura. Ma poi di nuovo, tieni presente che cose come i seeders di dati di esempio di solito devono essere veloci.

Quindi, anche se il codice sopra viene eseguito linearmente, la struttura del codice rappresenta la struttura "del mondo reale" degli oggetti, rendendo così più facile per gli altri sviluppatori la comprensione, la manutenzione e l'estensione.

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.