Чому для пробного лову потрібні дужки?


38

На різних мовах (принаймні Java, думаю, також C #?) Ви можете робити такі речі

if( condition )
    singleStatement;

while( condition )
    singleStatement;

for( var; condition; increment )
    singleStatement;

Тож, коли у мене є лише одне твердження, мені не потрібно додавати нову область { }. Чому я не можу зробити це за допомогою пробного лову?

try
    singleStatement;
catch(Exception e)
    singleStatement;

Чи є щось особливе у пробному лові, що вимагає завжди мати нову сферу чи щось таке? І якщо так, чи не міг компілятор це виправити?


3
Збирання ніт тут: Строго кажучи, для forчастин слід називати щось на зразок initial, conditionі stepяк initialне потрібно визначати змінну і stepне повинно бути збільшення.
Йоахім Зауер

4
як бічна примітка D не потрібні дужки навколо одного твердження, спробуйте блокувати блоки
ratchet freak

13
Я думаю, що справжнє питання - навпаки: чому деякі конструкції допускають «голі» заяви? Брекети повинні бути обов'язковими скрізь для консистенції.
UncleZeiv

3
@UncleZeiv - це непослідовно, якщо врахувати, що те, що випливає з цього, if- це завжди одне твердження, а кілька заяв, доданих дужками, складають одне твердження. Але я одна з тих, хто завжди ставить підтяжки в будь-якому випадку, тому ...
детлей

1
Я також схильний писати дужки, але мені подобається не писати їх, коли у мене є прямі точки виходу, як throwі return, і як @detly сказав, якщо ви розглядаєте дужки як єдину групу висловлювань, я не знаходжу це непослідовним також. Я ніколи не розумів, що таке "багато помилок кодування", про які згадували люди. Людям потрібно почати звертати увагу на те, що вони роблять, користуватися належним відступом і проводити одиничні тести: P ніколи з цим не було проблем ...
Свиш

Відповіді:


23

IMO, вони включені в 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_clause: 'catch' catch_arg statement

vs.

catch_clause: 'catch' catch_arg compound_statement

Визначення повного блоку спробу / лову взагалі не потрібно змінювати. У будь-якому випадку це буде щось на кшталт:

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. Для аналізатора / компілятора завдання є однаково легким (або складним) у будь-якому випадку. Незважаючи на це, @ відповідь Тома (і кількість підвищують голосів , якими вона отримала) забезпечує достатню демонстрацію того факту , що така зміна б майже напевно заплутати користувач.


ОП запитує про дужки навколо блоку спробу та вилову , в той час як це, мабуть, посилається на ті навколо спроби (перший абзац можна зрозуміти як посилання на обидва, але код ілюструє лише попередній) ... Чи можете ви уточнити?
Shog9

@ Mr.CRT: "блок лову" інакше буде відомий як "обробник", про який дивіться цитату вище.
Джеррі Труну

3
так, я просто відредагував мій коментар, щоб усунути цю двозначність Я розумію, що це може бути більш ефективною відповіддю, ніж Том (вище), якби це пішло на більшу довжину, щоб проілюструвати, як могла б працювати конструкція, що не має дужок , але заплутано (здається, коментар Біллі до відповіді Тома маючи на увазі, що це не могло б спрацювати, що я вважаю неправильним).
Shog9

@JerryCoffin Дякую за розширення вашої відповіді: зараз мені це набагато зрозуміліше.

try return g(); catch(xxii) error("G() goofed: xxii");мав би бути охайним досі IMO
alfC

19

У відповідь про те, чому потрібні дужки для деяких конструкцій з одним твердженням, а не для інших , Ерік Ліпперт написав:

Є ряд місць, де C # вимагає обмеженого блоку висловлювань, а не дозволу "голого" твердження. Вони є:

  • тіло методу, конструктора, деструктора, доступу до властивостей, події або індексатора.
  • блок спроби, зловити, нарешті, перевірений, неперевірений або небезпечний регіон.
  • блок заяви лямбда або анонімний метод
  • блок оператора 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, але не для спроби / лову ?
Діпан Мехта

3
@Dipan справедливий пункт. Цікаво, чи просто питання Java / C # намагається бути узгодженим із більш старими мовами, такими як C, дозволяючи невизначений ifs. Тоді як try / catch - це новіша конструкція, тому мовні дизайнери вважають нормальним порушити традицію.
Том Джефферіс

Я намагався відповісти на примус принаймні для C і C ++.
Dipan Mehta

6
@DipanMehta: Тому що для цього ifвипадку не існує декількох можливих вивішених пропозицій . Неважко просто сказати «добре, що інше прив'язується до найпотаємнішого, якщо», і зробити це з цим. Але для спробувати / ловити це не працює.
Біллі ONeal

2
@Billy: Я думаю, що ти переживаєш над потенційною неоднозначністю, яка присутня в, якщо / якщо інше ... Легко сказати "легко просто сказати" - але це тому, що існує жорстке правило для вирішення неоднозначності, яке, до речі, відключає певні конструкції без використання дужок. Відповідь Джеррі передбачає, що це був свідомий вибір, зроблений, щоб уникнути плутанини, але, безумовно, це могло спрацювати - так, як це "працює", якщо / якщо ще.
Shog9

1

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.