Почему Try-Catch требует фигурных скобок - программирование
Подтвердить что ты не робот

Почему Try-Catch требует фигурных скобок

Просто любопытно: почему синтаксис try catch в С# (также Java) жестко запрограммирован для нескольких операторов? Почему язык не позволяет:

int i;
string s = DateTime.Now.Seconds % 2 == 1 ? "1" : "not 1";
try
   i = int.Parse(s);
catch
   i = 0;

Пример только для тривиальных целей. Я знаю там int.TryParse.

4b9b3361

Ответ 1

Обратите внимание на то, что здесь есть три (или более) кодовых блока:

try {}
catch (myexcption)
{}
catch (myotherexception)
{}
finally
{}

Имейте в виду, что они находятся в области большего контекста, и исключения, которые не были пойманы, потенциально попадают в стек вверх.

Обратите внимание, что это в основном то же самое, что и конструкция класса, которая также имеет структуру {}.

Скажите, например, что у вас может быть:

try
try
if (iAmnotsane)
beatMe(please);
catch (Exception myexception)
catch (myotherexception)
logerror("howdy")
finally

СЕЙЧАС этот второй улов принадлежит первой или второй попытке? Как насчет наконец? Итак, вы видите, что необязательная/множественная часть выполняет требование.

Ответ 2

ОБНОВЛЕНИЕ: Этот вопрос был предметом моего блога 4 декабря 2012 года. Есть целый ряд проницательных комментариев в блоге, которые могут вас заинтересовать. Спасибо за отличный вопрос!


Как отмечали другие, предлагаемая функция вводит неопределенности, которые сбивают с толку. Мне было интересно узнать, есть ли другие основания для решения не поддерживать эту функцию, поэтому я проверил архив заметок на языке.

Я ничего не вижу в архиве заметок на языке, который оправдывает это решение. Насколько я знаю, С# делает это именно так, потому что это делают другие языки с похожим синтаксисом, и они делают это именно из-за проблемы двусмысленности.

Я действительно узнал что-то интересное. В начальном дизайне С# не было ни одного try-catch-finally! Если вы хотите попробовать с уловкой и, наконец, тогда вам нужно было написать:

try
{
  try
  {
      XYZ();
  }
  catch(whatever)
  {
     DEF();
  }
}
finally
{
  ABC();
}

что неудивительно, что именно компилятор анализирует try-catch-finally; он просто разбивает его на try-catch внутри try-finally после первоначального анализа и делает вид, что вы сказали в первую очередь.

Ответ 3

Более или менее, это игра на проблема с болтающимся другом.

Например,

if( blah )
    if ( more blah )
        // do some blah
else
    // no blah I suppose

Без фигурных скобок else является неоднозначным, потому что вы не знаете, связано ли это с первым или вторым оператором if. Таким образом, вы должны отказаться от соглашения о компиляторе (например, в Pascal или C, компилятор предполагает, что dangling else связан с ближайшим оператором if) для устранения неоднозначности или полностью скомпилировать компиляцию, если вы не хотите допускать такую ​​двусмысленность в первую очередь.

Аналогично,

try
    try
        // some code that throws!
catch(some blah)
    // which try block are we catching???
catch(more blah )
    // not so sure...
finally
    // totally unclear what try this is associated with.

Вы можете решить это с помощью соглашения, где блоки catch всегда связаны с ближайшей попыткой, но я нахожу это решение, как правило, позволяет программистам писать потенциально опасный код. Например, в C это:

if( blah )
    if( more blah )
        x = blah;
    else
        x = blahblah;

... как компилятор будет интерпретировать этот блок if/if/else. Тем не менее, это также совершенно законно, чтобы испортить ваш отступ и написать:

if( blah )
    if( more blah )
        x = blah;
else
    x = blahblah;

..., который теперь делает его похожим на else, связанным с внешним if, если на самом деле он связан с внутренним if-выражением из-за ограничений C. Поэтому я думаю, что требующие брекеты долгий путь к решению двусмысленности и предотвращению довольно скрытой ошибки (такие проблемы могут быть простыми, даже во время проверки кода). Языки, подобные python, не имеют этой проблемы, поскольку отступы и пробелы.

Ответ 4

Если вы предполагаете, что разработчики С# просто решили использовать тот же синтаксис, что и C++, то возникает вопрос, зачем нужны фигурные скобки с одиночными операторами, пытающимися и перехватывающими блоки в C++. Простой ответ заключается в том, что Бьярн Страуструп думал, что синтаксис легче объяснить.

В Проекте и Развитии C++ Страуструп пишет:

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

Далее он приводит пример, в котором ключевое слово try и {} не нужны. Затем он пишет:

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

Ссылка: Страуструп, Бьярне (1994). Дизайн и эволюция C++. Addison-Wesley.

Ответ 5

Первое, о чем я могу думать, это то, что фигурные скобки создают блок со своей областью переменных.

Посмотрите на следующий код

try
{
    int foo = 2;
}
catch (Exception)
{
    Console.WriteLine(foo); // The name 'foo' does not exist in the current context
}

foo недоступен в блоке catch из-за изменения переменной. Я думаю, что это облегчает рассуждение о том, была ли инициализирована переменная перед использованием или нет.

Сравните с этим кодом

int foo;
try
{
    foo = 2;
}
catch (Exception)
{
    Console.WriteLine(foo); // Use of unassigned local variable 'foo'
}

здесь вы не можете гарантировать, что foo инициализируется.

Ответ 6

try // 1
try // 2
  something();
catch { // A
}
catch { // B
}
catch { // C
}

делает ли B улавливает 1 или 2?

Я не думаю, что вы можете решить это однозначно, поскольку фрагмент может означать:

try // 1
{
    try // 2
        something();
    catch { // A
    }
}
catch { // B
}
catch { // C
}


try // 1
{
    try // 2
        something();
    catch { // A
    }
    catch { // B
    }
}
catch { // C
}

Ответ 7

Рациональное заключается в том, что он более удобен в обслуживании (легче изменить, с меньшей вероятностью сломать, эрго более высокое качество):

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

Что касается того, почему обработка исключений отличается от условных выражений...

  • Если /Else зависит от выражения использовать один из двух (или более If/Else if/Else) путей в коде
  • Try/Catch является частью обработки исключений, это не условное выражение. Try/Catch/finally работает только тогда, когда исключение выбрано внутри области Try.

Обработка исключений будет проходить по стеку/области до тех пор, пока не найдет блок Catch, который будет захватывать тип исключенного исключения. Принуждение идентификаторов областей делает эту проверку для блоков упрощенной. Принуждение к охвату при работе с исключениями кажется хорошей идеей, это также хороший признак того, что это часть обработки исключений, а не обычного кода. Исключения - это исключения, а не то, что вы действительно хотите делать нормально, но знаете, что может случиться и хотеть справиться, когда это произойдет.

EDIT: Есть еще одна причина, о которой я могу думать, это то, что CATCH является обязательным после TRY, в отличие от ELSE. Следовательно, должен быть определенный способ определения блока TRY.

Ответ 8

Возможно, чтобы препятствовать чрезмерному использованию. Блок try-catch большой и уродливый, и вы заметите, когда используете его. Это отражает эффект, который улов имеет на производительность вашего приложения - перехват исключения чрезвычайно медленный по сравнению с простым булевым тестом.

В общем, вы должны избегать ошибок, а не обрабатывать их. В примере, который вы приводите, гораздо более эффективным методом было бы использовать

if(!int.TryParse(s, out i))
 i=0;

Ответ 9

Еще один способ взглянуть на это...

Учитывая все проблемы обслуживания, которые были созданы операциями "if", "while", "for" и "foreach" без оснований, многие компании имеют стандарты кодирования, которые всегда требуют оснований для операторов, которые действуют на "блок".

Поэтому они заставляют вас писать:

if (itIsSo)
{
ASingleLineOfCode();
}

Вместо этого:

if (itIsSo)
ASingleLineOfCode();

(Примечание, поскольку отступ не проверяется компилятором, на него нельзя полагаться правильно)

Хороший случай может быть сделан для разработки языка, который всегда требует оснований, но тогда слишком много людей ненавидели бы С# из-за необходимости всегда использовать базы. Однако для try/catch не ожидалось, что вы сможете уйти, не используя базы, поэтому их можно было требовать, чтобы многие люди не жаловались.


Учитывая выбор, я бы предпочел бы, если /endIf (и while/endWhile) в качестве разделителей блоков, но США пошли по этому пути. (C определил, что большинство языков выглядит, а не Module2, после чего большая часть того, что мы делаем, определяется историей, а не логикой)

Ответ 10

Самый простой (я думаю) ответ: каждый блок кода в C/С++/С# требует фигурных скобок.

РЕДАКТИРОВАТЬ № 1

В ответ на отрицательные голоса, непосредственно из MSDN:

try-catch (ссылка С#)

Оператор try-catch состоит из try block, за которым следует одно или несколько кластеров catch, которые определяют обработчики для разных исключений.

Согласно определению, это блок, поэтому для этого требуются фигурные скобки. Вот почему мы не можем использовать его без { }.