Почему Try-Catch требует фигурные скобки - PullRequest
51 голосов
/ 22 октября 2010

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

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

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

Ответы [ 10 ]

68 голосов
/ 22 октября 2010

Примите во внимание тот факт, что на самом деле в игре находятся три (или более) кодовых блока:

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

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

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

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

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

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

50 голосов
/ 22 октября 2010

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


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

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

Хотя я узнал кое-что интересное. В первоначальном дизайне C # не было никаких попыток поймать-наконец! Если вы хотели попробовать с уловом и наконец, вам нужно было написать:

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

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

8 голосов
/ 23 октября 2010

Более или менее, это игра на висящей задаче .

Например,

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

Без фигурных скобок остальное неоднозначно, потому что вы не знаете, связано ли это с первым или вторым оператором if. Таким образом, вы должны откатиться на соглашение компилятора (например, в Pascal или C, компилятор предполагает, что зависание еще связано с ближайшим оператором 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 / else блок. Тем не менее, это также вполне законно, чтобы испортить ваш отступ и написать:

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

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

7 голосов
/ 23 октября 2010

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

В Дизайн и развитие C ++ Страуструп пишет:

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

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

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

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

1 голос
/ 22 октября 2010
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
}
1 голос
/ 22 октября 2010

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

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

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

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

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

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

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

0 голосов
/ 28 октября 2010

Другой взгляд на это ...

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

Итак, они заставляют тебя написать:

if (itIsSo)
{
ASingleLineOfCode();
}

Скорее тогда:

if (itIsSo)
ASingleLineOfCode();

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

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


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

0 голосов
/ 22 октября 2010

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

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

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

try-catch (C # Reference)

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

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

0 голосов
/ 22 октября 2010

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

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

if(!int.TryParse(s, out i))
 i=0;
0 голосов
/ 22 октября 2010

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

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

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

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

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

РЕДАКТИРОВАТЬ: Есть еще одна причина, о которой я могу думать, это то, что CATCH является обязательным послеПОПРОБУЙТЕ в отличие от ЕЩЕ.Следовательно, должен быть определенный способ определения блока TRY.

...