Perché sono necessarie le parentesi per il try-catch?

38

In varie lingue (almeno Java, pensa anche C #?) puoi fare cose come

if( condition )
    singleStatement;

while( condition )
    singleStatement;

for( var; condition; increment )
    singleStatement;

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

try
    singleStatement;
catch(Exception e)
    singleStatement;

C'è qualcosa di speciale nel try-catch che richiede sempre un nuovo scope o qualcosa del genere? E se sì, il compilatore non potrebbe risolvere il problema?

    
posta Svish 03.11.2011 - 14:49
fonte

4 risposte

23

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

The try keyword is completely redundant and so are the { } brackets except where multiple statements are actually used in a try-block or a handler. For example, it would have been trivial to allow:

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

However, I found this so difficult to explain that the redundancy was introduced to save support personnel from confused users.

Modifica: Per quanto riguarda il motivo per cui questo sarebbe confuso, penso che si debbano solo guardare le asserzioni errate nella risposta di @ Tom Jeffery (e, soprattutto, il numero di voti positivi ricevuti) per capire che ci sarebbe stato un problema. Per il parser, questo non è molto diverso dall'abbinamento di else s con if s - mancando le parentesi per forzare l'altro raggruppamento, tutte catch le clausole corrisponderebbero al più recente throw . Per quei linguaggi ingannevoli che lo includono, le clausole finally farebbero lo stesso. Dal punto di vista del parser, questo non è abbastanza diverso dalla situazione attuale da notare - in particolare, come stanno le grammatiche ora, non c'è davvero nulla per raggruppare le clausole catch - le parentesi raggruppano le dichiarazioni controllate dal % clausole dicatch, non le clausole di cattura stesse.

Dal punto di vista della scrittura di un parser, la differenza è quasi troppo piccola da notare. 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

La definizione di un blocco try / catch completo non dovrebbe comunque essere modificata. Ad ogni modo sarebbe qualcosa di simile:

catch_clauses: 
             | catch_clauses catch_clause
             ;

try_block: try_clause catch_clauses [finally_clause]
         ;

[Qui sto usando [whatever] per indicare qualcosa di opzionale, e sto tralasciando la sintassi per un finally_clause dal momento che non penso che abbia alcun impatto sulla domanda.]

Anche se non provi a seguire tutte le definizioni della grammatica Yacc-like, il punto può essere riassunto abbastanza facilmente: quell'ultima istruzione (che inizia con try_block ) è quella in cui le clausole catch vengono confrontate con clausole try - e rimane esattamente uguale se le parentesi sono richieste o meno.

Per reiterare / riepilogare: le parentesi raggruppano insieme le istruzioni controllate da il catch s, ma fanno non raggruppano il catch s stessi. In quanto tali, queste parentesi hanno assolutamente un effetto no al momento di decidere quale catch debba andare con quale try . Per il parser / compilatore l'attività è ugualmente facile (o difficile) in entrambi i casi. Nonostante ciò, @ la risposta di Tom (e il numero di voti positivi ricevuti) fornisce un'ampia dimostrazione del fatto che un simile cambiamento vorrebbe quasi certamente confondere gli utenti.

    
risposta data 03.11.2011 - 17:10
fonte
19

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

There are a number of places where C# requires a braced block of statements rather than allowing a "naked" statement. They are:

  • the body of a method, constructor, destructor, property accessor, event accessor or indexer accessor.
  • the block of a try, catch, finally, checked, unchecked or unsafe region.
  • the block of a statement lambda or anonymous method
  • the block of an if or loop statement if the block directly contains a local variable declaration. (That is, "while (x != 10) int y = 123;" is illegal; you've got to brace the declaration.)

In each of these cases it would be possible to come up with an unambiguous grammar (or heuristics to disambiguate an ambiguous grammar) for the feature where a single unbraced statement is legal. But what would the point be? In each of those situations you are expecting to see multiple statements; single statements are the rare, unlikely case. It seems like it is not realy worth it to make the grammar unambiguous for these very unlikely cases.

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

    
risposta data 03.11.2011 - 19:18
fonte
13

Penso che sia per evitare di ciondolare altri problemi di stile. Quello che 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 {

} 

Oppure ...

try {
   try {

   } catch(Exception e1) {

   } 
} catch(Exception e2) {

} finally {

} 
    
risposta data 03.11.2011 - 15:33
fonte
1

Penso che la ragione principale sia che c'è molto poco che puoi fare in C # che avrebbe bisogno di un blocco try / catch che è solo una riga. (Non riesco a pensare a nessuno in questo momento 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 {}.

    
risposta data 03.11.2011 - 14:54
fonte

Leggi altre domande sui tag