Warum werden Klammern für den Try-Catch benötigt?

38

In verschiedenen Sprachen (Java zumindest, denken Sie auch C #?) Können Sie Dinge wie

if( condition )
    singleStatement;

while( condition )
    singleStatement;

for( var; condition; increment )
    singleStatement;

Wenn ich also nur eine Anweisung habe, muss ich keinen neuen Bereich hinzufügen { }. Warum kann ich das nicht mit try-catch machen?

try
    singleStatement;
catch(Exception e)
    singleStatement;

Gibt es etwas Besonderes an Try-Catch, das immer einen neuen Umfang erfordert oder so? Und wenn ja, konnte der Compiler das nicht beheben?

Svish
quelle
3
Nit-Picking hier: Genau genommen sollte für fordie Teile so etwas heißen initial, conditionund stepda initialmuss keine Variable definiert und stepkein Inkrement sein.
Joachim Sauer
4
Als Randnotiz benötigt D keine geschweiften Klammern um die einzelne Anweisung. Versuchen Sie catch blocks
Ratschenfreak
13
Ich denke, die eigentliche Frage ist umgekehrt: Warum erlauben einige Konstrukte "nackte" Aussagen? Klammern sollten aus Gründen der Konsistenz überall obligatorisch sein.
UncleZeiv
3
@UncleZeiv - es ist nicht inkonsistent, wenn Sie bedenken, dass das, was folgt, ifimmer eine einzelne Anweisung ist und mehrere in geschweifte Klammern gesetzte Anweisungen eine einzelne Anweisung umfassen. Aber ich bin einer von denen, die sowieso immer die Zahnspange tragen, also ...
Detly,
1
Ich neige dazu, die geschweiften Klammern ebenfalls zu schreiben, aber ich mag es nicht, sie schreiben zu müssen, wenn ich direkte Austrittspunkte wie throwund return, und wie @detly sagte, wenn Sie die geschweiften Klammern als eine einzelne Gruppe von Anweisungen betrachten, finde ich es nicht inkonsistent entweder. Ich habe nie verstanden, was diese "vielen Codierungsfehler" sind, von denen die Leute sprechen. Die Leute müssen anfangen, auf das zu achten, was sie tun, die richtigen Einrückungen anwenden und Unit-Tests durchführen: P hatte nie ein Problem damit ...
4.

Antworten:

23

IMO, sie sind hauptsächlich in Java und C # enthalten, weil sie bereits in C ++ existierten. Die eigentliche Frage ist also, warum C ++ so ist. Nach dem Entwurf und der Entwicklung von C ++ (§16.3):

Das trySchlüsselwort ist vollständig redundant, ebenso wie die { }Klammern, außer wenn mehrere Anweisungen tatsächlich in einem try-Block oder einem Handler verwendet werden. Zum Beispiel wäre es trivial gewesen, Folgendes zuzulassen:

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

Ich fand dies jedoch so schwierig zu erklären, dass die Redundanz eingeführt wurde, um Supportpersonal vor verwirrten Benutzern zu schützen.

Edit: Was den Grund für die Verwirrung angeht, muss man meiner Meinung nach nur die falschen Behauptungen in @Tom Jefferys Antwort (und insbesondere die Anzahl der erhaltenen Up-Votes) betrachten, um zu erkennen, dass es ein Problem geben würde. Für den Parser ist dies nicht anders als das Abgleichen von elses mit ifs - ohne geschweifte Klammern, um eine andere Gruppierung zu erzwingen, würden alle catch Klauseln mit den neuesten übereinstimmen throw. Für jene missverstandenen Sprachen, die es enthalten, finallywürden Klauseln dasselbe tun. Aus Sicht des Parsers unterscheidet sich dies kaum genug von der aktuellen Situation, um zu bemerken - insbesondere gibt es nach heutigem Stand der Grammatik nichts, um die catchKlauseln zu gruppieren - die Klammern gruppieren die Anweisungen, die von der gesteuert werdencatch Klauseln, nicht die catch-Klauseln selbst.

Aus der Sicht, einen Parser zu schreiben, ist der Unterschied fast zu gering, um es zu bemerken. Wenn wir mit so etwas beginnen:

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

statement: compound_statement
         | simple_statement
         ;

statements: 
          | statements statement
          ;

compound_statement: '{' statements '}'

catch_arg: '(' argument ')'

Dann wäre der Unterschied zwischen:

try_clause: 'try' statement

und:

try_clause: 'try' compound_statement

Ebenso für catch-Klauseln:

catch_clause: 'catch' catch_arg statement

gegen

catch_clause: 'catch' catch_arg compound_statement

Die Definition eines vollständigen Try / Catch-Blocks müsste sich jedoch nicht ändern. In jedem Fall wäre es so etwas wie:

catch_clauses: 
             | catch_clauses catch_clause
             ;

try_block: try_clause catch_clauses [finally_clause]
         ;

[Hier gebe ich [whatever]etwas Optionales an und lasse die Syntax für a aus, finally_clauseda ich glaube, dass sie keinen Einfluss auf die Frage hat.]

Auch wenn Sie dort nicht versuchen, alle Yacc-ähnlichen Grammatikdefinitionen zu befolgen, lässt sich der Punkt ziemlich einfach zusammenfassen: Diese letzte Anweisung (beginnend mit try_block) ist diejenige, bei der catchKlauseln mit tryKlauseln abgeglichen werden - und sie bleibt genau die gleich, ob die Klammern erforderlich sind oder nicht.

Um es zu wiederholen / Fassen wir zusammen: die Klammern Gruppe zusammen die gesteuerten Aussagen durch die catchs, aber nicht Gruppe der catchs selbst. Als solche haben diese Klammern absolut keinen Einfluss auf die Entscheidung , welche catchmit dem geht try. Für den Parser / Compiler ist die Aufgabe in beiden Fällen gleichermaßen einfach (oder schwierig). Trotzdem zeigt die Antwort von @ Tom (und die Anzahl der abgegebenen Stimmen), dass eine solche Änderung die Benutzer mit ziemlicher Sicherheit verwirren würde .

Jerry Sarg
quelle
Die OP bittet um die Klammern um sowohl dem Versuch und der catch - Block , während dies scheint Referenzierung werden die um den Versuch (beide verweisen könnte der erste Absatz verstanden werden, aber der Code zeigt nur die ehemaligen) ... Können Sie klären?
Shog9
@ Mr.CRT: Ein "catch block" wäre ansonsten als "handler" bekannt, worüber man das obige Zitat sieht.
Jerry Coffin
3
bah, habe gerade meinen Kommentar bearbeitet, um diese Mehrdeutigkeit zu beseitigen . Worauf ich hinaus will, ist, dass dies eine effektivere Antwort sein könnte als die von Tom (oben), wenn es ausführlicher dargestellt würde, wie das Konstrukt ohne Klammern hätte funktionieren können, aber auf verwirrende Weise (Billys Kommentar zu Toms Antwort scheint zu sein) zu implizieren, dass es nicht hätte funktionieren können, was meiner Meinung nach falsch ist).
Shog9
@ JerryCoffin Vielen Dank für die Erweiterung Ihrer Antwort: Es ist mir jetzt viel klarer.
try return g(); catch(xxii) error("G() goofed: xxii");Wäre noch ordentlich gewesen IMO
AlfC
19

In einer Antwort darauf, warum für einige Konstrukte mit nur einer Anweisung Klammern erforderlich sind, andere jedoch nicht , schrieb Eric Lippert:

Es gibt eine Reihe von Stellen, an denen C # einen geschweiften Block von Anweisungen erfordert, anstatt eine "nackte" Anweisung zuzulassen. Sie sind:

  • Der Hauptteil einer Methode, eines Konstruktors, eines Destruktors, eines Eigenschaftszugriffsmoduls, eines Ereigniszugriffsmoduls oder eines Indexerzugriffsmoduls.
  • Der Block eines Try, Catch, Endlich, Checked, Unchecked oder Unsicheren Bereichs.
  • der Block einer Anweisung Lambda oder anonyme Methode
  • der Block einer if- oder loop-Anweisung, wenn der Block direkt eine lokale Variablendeklaration enthält. (Das heißt, "while (x! = 10) int y = 123;" ist illegal; Sie müssen die Deklaration einklammern.)

In jedem dieser Fälle wäre es möglich, eine eindeutige Grammatik (oder Heuristik, um eine mehrdeutige Grammatik zu disambiguieren) für das Merkmal zu entwickeln, bei dem eine einzelne nicht umrandete Aussage zulässig ist. Aber worum geht es? In jeder dieser Situationen erwarten Sie mehrere Anweisungen. einzelne Aussagen sind der seltene, unwahrscheinliche Fall. Es scheint sich nicht wirklich zu lohnen, die Grammatik für diese sehr unwahrscheinlichen Fälle eindeutig zu machen.

Mit anderen Worten, es war für das Compilerteam teurer, es zu implementieren, als es gerechtfertigt war, und dies zu dem Grenznutzen, den es bieten würde.

Robert Harvey
quelle
13

Ich denke, es ist zu vermeiden, andere Stilprobleme baumeln zu lassen. Folgendes wäre mehrdeutig ...

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?

Das könnte bedeuten:

try {
   try {

   } catch(Exception e1) {

   } catch(Exception e2) {

   } 
} finally {

} 

Oder...

try {
   try {

   } catch(Exception e1) {

   } 
} catch(Exception e2) {

} finally {

} 
Tom Jefferys
quelle
24
Diese Mehrdeutigkeit gilt für wenn und für gleichermaßen. Die Frage ist: Warum ist es für if- und switch- Anweisungen zulässig , aber nicht für try / catch ?
Dipan Mehta
3
@ Dipan fair point. Ich frage mich, ob es einfach darum geht, dass Java / C # versucht, mit älteren Sprachen wie C konsistent zu sein, indem es nicht geschweifte ifs zulässt. Während try / catch ein neueres Konstrukt ist, hielten es die Sprachdesigner für in Ordnung, mit der Tradition zu brechen.
Tom Jefferys
Ich habe versucht, den Zwang zumindest für C und C ++ zu beantworten.
Dipan Mehta
6
@ DipanMehta: Weil es für den ifFall nicht mehrere mögliche Dangling else-Klauseln gibt . Es ist einfach zu sagen "Nun, das Andere bindet an das Innerste, wenn" und damit fertig zu sein. Aber für try / catch funktioniert das nicht.
Billy ONeal
2
@Billy: Ich denke, Sie beschönigen die mögliche Mehrdeutigkeit in if / if else ... Es ist einfach zu sagen, "es ist einfach zu sagen" - aber das liegt daran, dass es eine harte Regel für die Lösung der Mehrdeutigkeit gibt, die im Übrigen nicht zulässig ist bestimmte Konstrukte ohne die Verwendung von Klammern. Jerry Antwort impliziert dies war eine bewusste Wahl zu vermeiden Verwirrung gemacht, aber sicher es könnte gearbeitet haben - so wie es „funktioniert“ für if / else if.
Shog9
1

Ich denke, der Hauptgrund ist, dass es in C # sehr wenig gibt, das einen Try / Catch-Block benötigt, der nur aus einer Zeile besteht. (Ich kann mir im Moment keine vorstellen). Sie können einen gültigen Punkt in Bezug auf den catch-Block haben, z. B. eine einzeilige Anweisung, um etwas zu protokollieren, aber in Bezug auf die Lesbarkeit ist es (zumindest für mich) sinnvoller, {} zu fordern.

Jetti
quelle