Perché sono necessarie le parentesi per try-catch?


38

In varie lingue (almeno Java, pensi anche in C #?) Puoi fare cose del genere

if( condition )
    singleStatement;

while( condition )
    singleStatement;

for( var; condition; increment )
    singleStatement;

Quindi, quando ho una sola affermazione, non ho bisogno di aggiungere un nuovo ambito con { }. Perché non posso farlo con try-catch?

try
    singleStatement;
catch(Exception e)
    singleStatement;

C'è qualcosa di speciale in try-catch che richiede sempre di avere un nuovo ambito o qualcosa del genere? E se è così, il compilatore non potrebbe risolverlo?


3
Nit-picking here: A rigor di termini per forle parti dovrebbe essere chiamato qualcosa di simile initial, conditione steppoiché initialnon è necessario definire una variabile e stepnon è necessario un incremento.
Joachim Sauer,

4
come nota a margine D non richiede parentesi graffe attorno alla singola istruzione try catch blocks
maniaco del cricchetto

13
Penso che la vera domanda sia al contrario: perché alcuni costrutti consentono dichiarazioni "nude"? Le parentesi graffe dovrebbero essere obbligatorie ovunque per coerenza.
ZioZeiv,

3
@UncleZeiv - non è incoerente se si considera che ciò che segue ifè sempre una singola istruzione e più istruzioni racchiuse tra parentesi graffe costituiscono una singola istruzione. Ma sono uno di quelli che mettono sempre le parentesi graffe, quindi ...
Detly

1
Tendo anche a scrivere le parentesi graffe, ma mi piace non doverle scrivere quando ho punti di uscita diretti come throwe return, e come @detly ha detto, se consideri le parentesi come un singolo gruppo di affermazioni, non lo trovo incoerente. Non ho mai capito quali siano questi "molti errori di codifica" menzionati dalle persone. Le persone hanno bisogno di iniziare a prestare attenzione a ciò che fanno, usare un'indentazione adeguata e fare test unitari: P non ha mai avuto problemi con questo ...
Svish

Risposte:


23

IMO, sono inclusi in Java e C # principalmente perché esistevano già in C ++. La vera domanda, quindi, è perché è C ++ in quel modo. Secondo The Design and Evolution of C ++ (§16.3):

La tryparola chiave è completamente ridondante, così come le { }parentesi, tranne nel caso in cui più istruzioni vengano effettivamente utilizzate in un blocco di prova o in un gestore. Ad esempio, sarebbe stato banale consentire:

int f()
{
    return g() catch(xxii) { // not C++
        error("G() goofed: xxii");
        return 22;
    };
}

Tuttavia, ho trovato così difficile spiegare che la ridondanza è stata introdotta per salvare il personale di supporto da utenti confusi.

Modifica: Per quanto riguarda il motivo per cui questo potrebbe essere fonte di confusione, penso che si debba solo guardare alle affermazioni errate nella risposta di @Tom Jeffery (e, soprattutto, il numero di voti positivi che ha ricevuto) per rendersi conto che ci sarebbe un problema. Per il parser, questo in realtà non è diverso dall'abbinare elses con ifs - mancano le parentesi graffe per forzare altri raggruppamenti, tutte le catch clausole corrisponderebbero al più recente throw. Per quelle lingue sbagliate che lo includono, le finallyclausole farebbero lo stesso. Dal punto di vista del parser, questo non è abbastanza diverso dalla situazione attuale da notare - in particolare, allo stato attuale delle grammatiche, non c'è davvero nulla per raggruppare le catchclausole - le parentesi raggruppano le dichiarazioni controllate dalcatch clausole, non le stesse clausole di cattura.

Dal punto di vista della scrittura di un parser, la differenza è quasi troppo piccola per essere notata. Se iniziamo con qualcosa del genere:

simple_statement: /* won't try to cover all of this */
                ;

statement: compound_statement
         | simple_statement
         ;

statements: 
          | statements statement
          ;

compound_statement: '{' statements '}'

catch_arg: '(' argument ')'

Quindi la differenza sarebbe tra:

try_clause: 'try' statement

e:

try_clause: 'try' compound_statement

Allo stesso modo, per le clausole di cattura:

catch_clause: 'catch' catch_arg statement

vs.

catch_clause: 'catch' catch_arg compound_statement

Tuttavia, la definizione di un blocco try / catch completo non dovrebbe assolutamente cambiare. In entrambi i casi sarebbe qualcosa del tipo:

catch_clauses: 
             | catch_clauses catch_clause
             ;

try_block: try_clause catch_clauses [finally_clause]
         ;

[Qui sto usando [whatever]per indicare qualcosa di facoltativo, e sto tralasciando la sintassi per un finally_clausedato che non penso che abbia alcun rapporto con la domanda.]

Anche se non provi a seguire tutta la definizione grammaticale simile a Yacc lì, il punto può essere riassunto abbastanza facilmente: l'ultima affermazione (a partire da try_block) è quella in cui le catchclausole vengono abbinate alle tryclausole - e rimane esattamente il lo stesso sia che le parentesi graffe siano necessarie o meno.

Per ribadire / Riassumendo: il gruppo di parentesi graffe insieme delle dichiarazioni controllate dai le catchs, ma farlo senza gruppo il catchloro s. Come tale, quelle parentesi graffe non hanno assolutamente alcun effetto nel decidere quale catchva con quale try. Per il parser / compilatore l'attività è ugualmente facile (o difficile) in entrambi i casi. Nonostante questo, @ risposta di Tom (e il numero di up-voti è ricevuto) fornisce ampia dimostrazione del fatto che tale cambiamento una avrebbe utenti quasi certamente Confuse.


L'OP sta chiedendo delle parentesi attorno sia al blocco try che a quello catch , mentre questo sembra fare riferimento a quelli intorno al try (il primo paragrafo potrebbe essere inteso come riferimento a entrambi, ma il codice illustra solo il primo) ... chiarire?
Shog9

@ Mr.CRT: un "blocco di cattura" sarebbe altrimenti noto come un "gestore", sul quale vedere la citazione sopra.
Jerry Coffin,

3
bah, ho appena modificato il mio commento per rimuoverlo quella ambiguità. Quello che sto ottenendo è che questa potrebbe essere una risposta più efficace di quella di Tom (sopra) se illustrasse come avrebbe potuto funzionare il costrutto senza parentesi , ma in modo confuso (il commento di Billy sulla risposta di Tom sembra implicare che non avrebbe potuto funzionare, il che ritengo errato).
Shog9

@JerryCoffin Grazie per aver ampliato la tua risposta: ora è molto più chiaro per me.

try return g(); catch(xxii) error("G() goofed: xxii");sarebbe stato ancora pulito IMO
alfC il

19

In una risposta sul perché sono necessarie parentesi per alcuni costrutti a singola istruzione ma non per altri , Eric Lippert ha scritto:

Esistono diversi punti in cui C # richiede un blocco di istruzioni rinforzato anziché consentire un'istruzione "nuda". Loro sono:

  • il corpo di un metodo, un costruttore, un distruttore, un acceditore di proprietà, un acceditore di eventi o un acceditore di indicizzatore.
  • il blocco di una regione try, catch, infine, selezionata, non selezionata o non sicura.
  • il blocco di un'istruzione lambda o metodo anonimo
  • il blocco di un'istruzione if o loop se il blocco contiene direttamente una dichiarazione di variabile locale. (Cioè, "while (x! = 10) int y = 123;" è illegale; devi rinforzare la dichiarazione.)

In ognuno di questi casi sarebbe possibile elaborare una grammatica non ambigua (o euristica per chiarire una grammatica ambigua) per la caratteristica in cui una singola affermazione senza limiti è legale. Ma quale sarebbe il punto? In ognuna di quelle situazioni ti aspetti di vedere più dichiarazioni; le singole dichiarazioni sono il caso raro e improbabile. Sembra che non valga davvero la pena di rendere inequivocabile la grammatica per questi casi molto improbabili.

In altre parole, era più costoso per il team del compilatore implementarlo di quanto non fosse giustificato, per il vantaggio marginale che avrebbe fornito.


13

Penso che sia per evitare penzolanti problemi di stile. Quanto segue sarebbe ambiguo ...

try
    // Do stuff
try
    // Do  more stuff
catch(MyException1 e1)
    // Handle fist exception
catch(MyException2 e2)
    // So which try does this catch belong to?
finally
    // and who does this finally block belong to?

Potrebbe significare questo:

try {
   try {

   } catch(Exception e1) {

   } catch(Exception e2) {

   } 
} finally {

} 

O...

try {
   try {

   } catch(Exception e1) {

   } 
} catch(Exception e2) {

} finally {

} 

24
Questa ambiguità si applica a se e per ugualmente. La domanda è: perché è consentito per if e switch statement ma non per try / catch ?
Dipan Mehta,

3
@Dipan punto giusto. Mi chiedo se è semplicemente una questione di Java / C # che cerca di essere coerente con linguaggi più vecchi come C, consentendo if non rinforzati. Considerando che try / catch è un costrutto più recente, quindi i progettisti del linguaggio hanno ritenuto giusto rompere con la tradizione.
Tom Jefferys,

Ho provato a rispondere alla compulsione almeno per C e C ++.
Dipan Mehta,

6
@DipanMehta: Perché non ci sono più possibili clausole penzolanti per il ifcaso. È facile dire "bene, l'altro si lega al più profondo se" e finirlo. Ma per provare / catturare che non funziona.
Billy ONeal,

2
@Billy: Penso che stai sorvolando la potenziale ambiguità presente in if / if else ... È facile dire "è facile solo dire" - ma è perché c'è una dura regola per risolvere l'ambiguità che, per inciso, non consente alcuni costrutti senza l'uso di parentesi. La risposta di Jerry implica che questa è stata una scelta consapevole fatta per evitare confusione, ma sicuramente avrebbe potuto funzionare - proprio come "funziona" per if / if else.
Shog9

1

Penso che il motivo principale sia che in C # c'è molto poco da fare che avrebbe bisogno di un blocco try / catch che sia solo una riga. (Non riesco a pensare a nessun momento della parte superiore della mia testa). Potresti avere un punto valido in termini di blocco catch, ad esempio un'istruzione di una riga per registrare qualcosa, ma in termini di leggibilità ha più senso (almeno per me) richiedere {}.

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.