Вариант использования для try-catch-finally с обеими функциями catch и finally - PullRequest
8 голосов
/ 17 февраля 2010

Я понимаю, как try-catch работает и как try-finally , но я использую их (обычно) в двух совершенно разных сценариях:

  • try-finally (или using в C # и VB) в основном используется вокруг некоторого кодового блока среднего размера, который использует некоторый ресурс, который должен быть правильно расположен.
  • try-catch в основном используется либо
    • вокруг одного оператора, который может быть очень неудачным или
    • (в качестве всеобъемлющего) на очень высоком уровне приложения, обычно непосредственно под действием пользовательского интерфейса.

По моему опыту, случаи, когда try-catch-finally были бы уместны, т.е. когда блок, в котором я хочу перехватить какое-то конкретное исключение, точно тот же блок в котором я использую какой-то одноразовый ресурс, крайне редко. Тем не менее, разработчики языка C # , VB и Java , похоже, считают это очень распространенным сценарием; дизайнеры VB даже думают о добавлении catch к using.

Я что-то упустил? Или я просто чрезмерно педантичен с моим ограниченным использованием try-catch ?


РЕДАКТИРОВАТЬ: Чтобы уточнить: мой код обычно выглядит так (функции развернуты для ясности):

Try
    do something
    Aquire Resource (e.g. get DB connection)
    Try 
        do something
        Try
            do something that can fail
        Catch SomeException
            handle expected error
        do something else... 
    Finally 
        Close Resource (e.g. close DB connection)
    do something
Catch all
    handle unexpected errors

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

Ответы [ 10 ]

16 голосов
/ 17 февраля 2010

Цитата из MSDN

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

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

Чтобы быть на 100% уверенными в том, что ресурсы утилизированы, вы используете блок finally, однако вам нужноточно указать, что в 1% случаев возникает ошибка, поэтому вам может потребоваться настроить ведение журнала в секции «catch-ing».

Это очень распространенный сценарий.

Edit - практический пример

Здесь есть несколько хороших примеров: Транзакции SQL с классом SqlTransaction .Это только один из многих способов использовать Try, Catch & Наконец, и он демонстрирует это очень хорошо, даже если using(var x = new SqlTranscation) может быть эффективен несколько раз.

Итак, пошло.

var connection = new SqlConnection();

var command = new SqlCommand();

var transaction = connection.BeginTransaction();

command.Connection = connection;
command.Transaction = transaction;

А вот и более интересные детали

///
/// Try to drop a database
///
try
{
    connection.Open();

    command.CommandText = "drop database Nothwind";

    command.ExecuteNonQuery();
}

Итак, давайте представим, что вышеперечисленное не работает по какой-то причине, и выдается исключение

///
/// Due to insufficient priviligies we couldn't do it, an exception was thrown
///
catch(Exception ex)
{
    transaction.Rollback();
}

Транзакция будет отменена!Помните, что изменения, внесенные вами в объекты внутри try / catch, не будут отменены, даже если вы вложите их в использование!

///
/// Clean up the resources
///
finally
{

    connection.Close();
    transaction = null;
    command = null;
    connection = null;
}

Теперь ресурсы очищены!

12 голосов
/ 17 февраля 2010

Не ответ на ваш вопрос, а забавный факт.

Реализация Microsoft компилятора C # на самом деле не может обрабатывать try-catch-finally. Когда мы разбираем код

try { Foo() } 
catch(Exception e) { Bar(e); }
finally { Blah(); }

мы фактически делаем вид, что код был написан

try 
{
   try { Foo(); }
   catch(Exception e) { Bar(e); }
}
finally { Blah(); }

Таким образом, остальные компоненты компилятора - семантический анализатор, средство проверки достижимости, генератор кода и т. Д. - имеют дело только с try-catch и try-finally, а не с try-catch-finally. На мой взгляд, немного глупая ранняя трансформация, но она работает.

10 голосов
/ 17 февраля 2010

Пример:

Try
   Get DB connection from pool
   Insert value in table
   do something else...
Catch
   I have an error... e.g.: table already contained the row I was adding
   or maybe I couldn't get the Connection at all???
Finally
   if DB connection is not null, close it.

Вы не можете получить более "общий" пример. Жаль, что некоторые люди до сих пор забывают поместить закрытое соединение в поле «Конец», где оно принадлежит, а не в блок «Пробовать» ...: (

6 голосов
/ 17 февраля 2010

Я часто пишу код, который выглядит следующим образом:

Handle h = ...
try {
   // lots of statements that use handle
} catch (SomeException ex) {
   // report exception, wrap it in another one, etc
} catch (SomeOtherException ex) {
   // ...
} finally {
   h.close();
}

Так что, может быть, вы просто чрезмерно педантичны ... например поместив попытку / поймать вокруг отдельных утверждений. (Иногда это необходимо, но по моему опыту вам обычно не нужно быть таким мелкозернистым.)

3 голосов
/ 17 февраля 2010

Я думаю, что вы совершенно правы.Из. Руководства по проектированию Net Framework , написанного некоторыми из ведущих архитекторов Microsoft:

НЕ перехват.Зачастую исключения должны распространяться вверх по стеку вызовов.

В хорошо написанном коде try-finally [или использование] встречается гораздо чаще, чем try-catch.Поначалу это может показаться нелогичным, но блоки ловли не нужны в удивительном количестве случаев.С другой стороны, вы всегда должны учитывать, может ли try-finally [или использование] быть полезным для очистки.

стр. 230, раздел 7.2

3 голосов
/ 17 февраля 2010

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

try {
    // some code
    SomeResource res = new SomeResource();
    try {
        res.use();
    } finally {
        res.close();
    }
    // some more code
} catch( Exception ex ) {
    handleError( ex );
}

Это закрывает ресурс как можно раньше в любом случае (ошибка или нет), но все равно обрабатывает все возможные ошибки при создании или использовании ресурса в одном блоке перехвата.

1 голос
/ 28 июля 2010

Как насчет чего-то вроде:

  Dim mainException as Exception = Nothing

  Try
    ... Start a transaction
    ... confirm the transaction
  Catch Ex as Exception
    mainException = Ex
    Throw
  Finally
    Try
      ... Cleanup, rolling back the transaction if not confirmed
    Catch Ex as Exception
      Throw New RollbackFailureException(mainException, Ex)
    End Try
  End Try

При условии, что здесь RollbackFailureException включает в себя поле «OriginalException», а также «InnerException», и принимает параметры для обоих. Никто не хочет скрывать тот факт, что исключение произошло во время отката, но также не хочет потерять исходное исключение, которое вызвало откат (и может дать некоторое представление о том, почему произошел откат).

1 голос
/ 17 февраля 2010

Я бы почти всегда использовал try-catch-finaly в тех случаях, когда вам необходимо во всех случаях что-то утилизировать, а вы используете этот случай для регистрации ошибки и / или информирования пользователя.

0 голосов
/ 17 февраля 2010

Другое использование может заключаться в размещении дескриптора файла во вложении электронной почты при использовании объектов почты System.Web.Mail для отправки электронной почты. Я узнал об этом, когда мне пришлось программно открыть отчет Crystal Report, сохранить его на диск, прикрепить к электронному письму, а затем удалить с диска. Явный .Dispose () потребовался в Final, чтобы убедиться, что я могу удалить его, особенно в случае сгенерированного исключения.

0 голосов
/ 17 февраля 2010

По моему опыту, случаи, когда try-catch-finally будет уместным, то есть когда блок, в котором я хочу поймать какое-то конкретное исключение, являются точно тем же блоком, в котором я использую какой-то одноразовый ресурс, крайне редки. Тем не менее, разработчики языка C #, VB и Java, похоже, считают это весьма распространенным сценарием; дизайнеры VB даже думают о добавлении улова к использованию.

Вы:

try {
   //use resource
} catch (FirstException e) {
   // dispose resource
   // log first exception
} catch (SecondException e) {
   // dispose resource
   // log first exception
} catch (ThirdException e) {
   // dispose resource
   // log first exception
}

мне:

try {
  //use resource
} catch (FirstException e) {
  // log first exception
} catch (SecondException e) {
  // log first exception
} catch (ThirdException e) {
  // log first exception
} finally {
  // dispose resource
}

испытываете чувство непослушания?)

...