Почему скобки требуются для try-catch?

38

На разных языках (по крайней мере, на Java, подумайте также C #?) Вы можете делать такие вещи, как

if( condition )
    singleStatement;

while( condition )
    singleStatement;

for( var; condition; increment )
    singleStatement;

Поэтому, когда у меня есть только одно утверждение, мне не нужно добавлять новую область с помощью { }. Почему я не могу сделать это с помощью try-catch?

try
    singleStatement;
catch(Exception e)
    singleStatement;

Есть ли что-то особенное в try-catch, которое требует всегда иметь новую область видимости или что-то в этом роде? И если так, не может ли компилятор это исправить?

Svish
источник
3
Пометка здесь: Строго говоря, для forчастей должно быть указано что-то вроде initial, conditionи, stepпоскольку нет initialнеобходимости определять переменную и stepне должно быть приращения.
Йоахим Зауэр
4
как примечание D не требует скобок вокруг одного оператора, попробуйте перехватить блоки
ratchet freak
13
Я думаю, что реальный вопрос заключается в обратном: почему некоторые конструкции допускают «голые» утверждения? Брекеты должны быть обязательными везде для последовательности.
UncleZeiv
3
@UncleZeiv - это не является противоречивым, если вы считаете, что то, что следует за if, всегда является одним утверждением, а несколько утверждений, заключенных в фигурные скобки, составляют одно утверждение. Но я один из тех, кто всегда ставит фигурные скобки в любом случае, так что ...
ловко
1
Я также склонен писать фигурные скобки, но мне не нужно писать их, когда у меня есть точки прямого выхода, такие как throwи return, и, как сказал @detly, если вы рассматриваете фигурные скобки как одну группу утверждений, я не нахожу это несовместим либо. Я никогда не понимал, что это за «множество ошибок кодирования», о которых говорили люди. Люди должны начать обращать внимание на то, что они делают, использовать правильные абзацы и проходить модульные тесты: у П никогда не было проблем с этим ...
Свиш

Ответы:

23

ИМО, они включены в Java и C # прежде всего потому, что они уже существовали в C ++. Таким образом, реальный вопрос заключается в том, почему C ++ таков. Согласно Проекту и Развитию C ++ (§16.3):

tryКлючевое слово полностью избыточное и поэтому являются { }скобками , за исключением , когда несколько операторов на самом деле используется в примерках блока или обработчик. Например, было бы тривиально разрешить:

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

Однако мне было так трудно объяснить, что избыточность была введена, чтобы уберечь персонал службы поддержки от запутанных пользователей.

Редактировать: Что касается того, почему это было бы непонятно, я думаю, что нужно только взглянуть на неправильные утверждения в ответе @Tom Jeffery (и, особенно, на количество полученных «за»), чтобы понять, что возникнет проблема. Для парсера это на самом деле ничем не отличается от сопоставления elses с ifs - без скобок для принудительного создания другой группировки все catch предложения будут совпадать с самыми последними throw. Для тех порожденных языков, которые включают его, finallyпункты будут делать то же самое. С точки зрения синтаксического анализатора, это вряд ли достаточно отличается от текущей ситуации, чтобы заметить - в частности, поскольку грамматики стоят сейчас, на самом деле нечего группировать catchпредложения вместе - скобки группируют операторы, контролируемыеcatch пункты, а не сами предложения.

С точки зрения написания парсера, разница почти слишком мала, чтобы заметить. Если мы начнем с чего-то вроде этого:

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

statement: compound_statement
         | simple_statement
         ;

statements: 
          | statements statement
          ;

compound_statement: '{' statements '}'

catch_arg: '(' argument ')'

Тогда разница будет между:

try_clause: 'try' statement

а также:

try_clause: 'try' compound_statement

Аналогично, для предложений catch:

catch_clause: 'catch' catch_arg statement

против

catch_clause: 'catch' catch_arg compound_statement

Хотя определение полного блока try / catch не нужно менять вообще. В любом случае это будет что-то вроде:

catch_clauses: 
             | catch_clauses catch_clause
             ;

try_block: try_clause catch_clauses [finally_clause]
         ;

[Здесь я использую, [whatever]чтобы указать что-то необязательное, и я опускаю синтаксис для, finally_clauseтак как я не думаю, что это имеет какое-либо отношение к вопросу.]

Даже если вы не пытаетесь следовать всем Yacc-как определение грамматики там, точку можно резюмировать довольно легко: это последнее утверждение (начиная с try_block) является тот , где catchпункты получить сопоставляется с tryпунктами - и остается именно То же самое, требуется ли скобки или нет.

Повторим / Подведем итог: скобки группа вместе операторы , контролируемые с помощью тех catchх, но делать не группировать catchсами с. Как таковые, эти скобки не имеют абсолютно никакого влияния на решение, которое catchидет с каким try. Для парсера / компилятора задача одинаково легка (или сложна) в любом случае. Несмотря на это, @ ответ Тома (и количество повышающих голосов , которыми она получила) обеспечивает достаточную демонстрацию того факта , что такое изменение бы почти наверняка запутать пользователь.

Джерри Гроб
источник
ОП спрашивает о скобках вокруг блока try и catch , в то время как это, кажется, ссылается на скобки вокруг try (первый абзац можно понять как ссылку на оба, но код иллюстрирует только первый) ... Можете ли вы прояснить?
Shog9
@ Mr.CRT: иначе «блок catch» будет известен как «обработчик», о чем см. Цитату выше.
Джерри Гроб
3
Ба, только что отредактировал мой комментарий, чтобы убрать эту двусмысленность. Я имею в виду, что это может быть более эффективным ответом, чем у Тома (см. Выше), если он пойдет дальше, чтобы проиллюстрировать, как конструкция без скобок могла бы работать, но в некоторой путанице (комментарий Билли на ответ Тома кажется подразумевать, что это не могло сработать, что я считаю неверным).
Shog9
@JerryCoffin Спасибо, что расширили свой ответ: теперь мне стало намного понятнее.
try return g(); catch(xxii) error("G() goofed: xxii");как бы это ни было хорошо ИМО
alfC
19

В ответе о том, почему скобки требуются для некоторых конструкций с одним утверждением, но не для других , Эрик Липперт написал:

Есть ряд мест, где C # требует ограниченного блока операторов вместо того, чтобы разрешать «голый» оператор. Они есть:

  • тело метода, конструктора, деструктора, средства доступа к свойствам, средства доступа к событиям или средства индексирования.
  • блок «попробуй, поймай, наконец, проверил, не проверял или небезопасен».
  • блок выражения лямбда или анонимный метод
  • блок оператора if или цикла if, если блок непосредственно содержит объявление локальной переменной. (То есть «while (x! = 10) int y = 123;» недопустимо; вы должны заключить декларацию.)

В каждом из этих случаев можно было бы придумать однозначную грамматику (или эвристику для устранения неоднозначности грамматической грамматики) для функции, в которой допустимо одно несвязанное утверждение. Но какой в ​​этом смысл? В каждой из этих ситуаций вы ожидаете увидеть несколько утверждений; отдельные заявления - редкий, маловероятный случай. Кажется, что не стоит делать грамматику однозначной для этих очень маловероятных случаев.

Другими словами, команде разработчиков было дороже реализовать ее, чем было оправдано, так как это дало бы предельную выгоду.

Роберт Харви
источник
13

Я думаю, что это нужно для того, чтобы избежать проблем со стилем. Следующее было бы неоднозначным ...

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?

Это может означать это:

try {
   try {

   } catch(Exception e1) {

   } catch(Exception e2) {

   } 
} finally {

} 

Или...

try {
   try {

   } catch(Exception e1) {

   } 
} catch(Exception e2) {

} finally {

} 
Том Джефферис
источник
24
Эта неопределенность касается , если и в равной степени. Вопрос в том, почему это разрешено для оператора if и switch, но не для try / catch ?
Дипан Мехта
3
@ Dipan справедливая точка. Интересно, это просто вопрос Java / C #, пытающийся быть совместимым со старыми языками, такими как C, разрешая ifs без скобок. Принимая во внимание, что try / catch является более новой конструкцией, поэтому дизайнеры языка решили, что можно порвать с традицией.
Том Джефферис
Я попытался ответить на принуждение по крайней мере для C и C ++.
Дипан Мехта
6
@DipanMehta: Потому что для данного ifслучая не существует нескольких возможных повисших других предложений . Легко просто сказать «хорошо, остальное связывает с самым внутренним, если» и покончить с этим. Но для try / catch это не работает.
Билли ONEAL
2
@ Билли: Я думаю, что вы скрываете потенциальную неоднозначность, присутствующую в том, если / если еще ... Легко сказать «просто сказать» - но это потому, что существует жесткое правило для устранения неоднозначности, которое, кстати, не допускает определенные конструкции без использования скобок. Ответ Джерри подразумевает, что это был сознательный выбор, сделанный, чтобы избежать путаницы, но, безусловно, он мог бы сработать - так же, как он «работает», если / если еще.
Shog9
1

Я думаю, что основная причина в том, что в C # мало что можно сделать, для чего понадобится блок try / catch, состоящий всего из одной строки. (Я не могу сейчас думать о вершине моей головы). У вас может быть правильная точка с точки зрения блока catch, например, однострочный оператор для записи чего-либо, но с точки зрения читабельности имеет смысл (по крайней мере для меня) требовать {}.

Jetti
источник