Какая польза от блока finally, которому предшествует универсальный блок catch, в C #? - PullRequest
11 голосов
/ 11 января 2010

Рассмотрим следующую структуру кода C # (S0-S3 являются заполнителями для произвольных блоков кода):

try
{
    S0;
}
catch (Exception ex)
{
    S1;
}
finally
{
    S2;
}

S3;

В случае, если S1 генерирует исключение внутри обработчика catch, S2 внутри finally все равно будет выполняться (но S3 не будет).

Вопрос

Предполагая, что S1 не может выбросить , есть ли смысл иметь S2 внутри блока finally, вместо того, чтобы иметь его вне try / catch / finally, непосредственно перед S3?

Пример

try
{
    // Do something that might throw
}
catch (Exception ex)
{
    // Save the exception to re-throw later
    // NB: This statement cannot throw an exception!
    this.cachedException = ex;
}
finally
{
    S2;
}

S3;

Есть ли смысл иметь блок finally? Не будет ли следующий код не эквивалентным (при строгом предположении, что то, что находится внутри блока catch, не может выдать):

try
{
    // Do something that might throw
}
catch (Exception ex)
{
    // Save the exception to re-throw later
    // NB: This statement cannot throw an exception!
    this.cachedException = ex;
}

// No finally block needed (?)
S2;
S3;

Вторичный вопрос

Обновление: Если будет принято, что два кодовых блока выше эквивалентны (согласно заявленным предположениям), то, учитывая обратную связь по ясности кода в ответах, будет ли это предпочтительным (и эквивалентным ) объединить S2 и S3 внутри блока finally?

try
{
    // Do something that might throw
}
catch (Exception ex)
{
    // Save the exception to re-throw later
    // NB: This statement cannot throw an exception!
    this.cachedException = ex;
}
finally
{
    S2; // Put S2 and S3 together inside the `finally` block to guard against
    S3; // future changes in the `catch` filter, or handling code.
}

Ответы [ 10 ]

12 голосов
/ 11 января 2010

Предположение, что S1 не может выбросить, является хрупким, учитывая сценарии истощения ресурсов (т. Е. У вас заканчивается память). Даже если это оправдано (большое if), незначительные изменения в коде могут привести к исключению.

Поскольку S2 обычно занимается очисткой и освобождением ценных ресурсов, размещение его в блоке finally четко сообщает об этом намерение . Размещение такого кода, где это возможно, в методе Dispose () объекта, владеющего ресурсом, и замене предложения try / finally на предложение using может даже лучше передать намерение (и более идиоматически для C #).

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

По второму вопросу: S3 должен быть помещен внутрь, если он связан с очисткой. Если это предполагает успех блока try, его следует поместить после блока finally. Если ваше предложение catch не сработает повторно, я лично интерпретировал бы его как означающее, что вы добились успеха и можете продолжить обычные операции. Тем не менее, целая вещь «Сохранить исключение, чтобы повторно выбросить позже» смущает меня. Как правило, я бы рекомендовал не хранить исключение для повторного выброса за пределы метода. Это необычно и кажется мне запутанным. Чем меньше сюрпризов содержит ваш код, тем легче его поддерживать (в том числе и себя, три месяца спустя).

10 голосов
/ 11 января 2010

Один случай, когда вы не ожидаете выброс S1: если поток прерывается, исключение будет автоматически переброшено в конце блока catch.

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

3 голосов
/ 11 января 2010

Я думаю, что это похоже на заключение в скобки, если содержимое оператора.

Пока можно утверждать, что

if (x) y;

не подведет, что сказать, что какой-то менее опытный программист не придет позже и отредактирует его до

if (x) y; z;

Может случиться, что блок finally не нужен для вашего кода в его нынешнем виде, но всегда полезно оставить его в случае, если код в блоке catch когда-либо изменится.

if (x) 
{
    y;
}

всегда побеждает для меня.

3 голосов
/ 11 января 2010

Обратите внимание, что предложение finally выполняется, даже если блок try или catch содержит оператор return.

3 голосов
/ 11 января 2010

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

2 голосов
/ 11 января 2010

В вашем конкретном случае нет разницы ..

Но если бы вы не уловили Exception, а какой-то конкретный подкласс Exception, тогда была бы разница. И это, вероятно, нормальный паттерн при просмотре блока try-catch-finally.

1 голос
/ 17 марта 2011

Чтобы гарантировать очистку ресурсов при возникновении исключения, используйте блок try / finally. Закройте ресурсы в пункте finally. Использование блока try / finally гарантирует, что ресурсы будут удалены, даже если произойдет исключение .... Для более подробной информации проверьте http://itpian.com/Coding/5143-Need-for-Finally-block.aspx

1 голос
/ 11 января 2010

Во втором случае, элемент управления достигнет S2 наверняка, только если вы проглотите любое исключение, когда-либо пойманное в блоке захвата! Try-Catch не следует использовать здесь и там, но осторожно.

Ex:

try
{
    // Do something that might throw
}
catch (Exception ex)
{
    // Save the exception to re-throw later
    // NB: This statement cannot throw an exception!
    // this.cachedException = ex;

    // Must swallow any exception here to let control go further!

    // If you're not sure enough [which you and me both are not likely to be] - use finally to execute S2 in any condition
}

// No finally block needed (?)
S2;
S3;
1 голос
/ 11 января 2010

Вы можете быть уверены, что ваш код не вызывает исключения сегодня, но как быть, когда вы вернетесь к нему через 6 месяцев? Или кто-то еще должен внести изменения?

Использование блока finally - это узнаваемый шаблон, который будет понятен любому программисту: если ваш код делает что-то особенное и отличное, он сбивает вас с толку или кого-то еще.

0 голосов
/ 11 января 2010

Нет, вам не нужно его использовать. Это не требуется в этом случае. Выбор за вами и зависит от ресурсов, которые вы можете / не можете использовать. Если вам нужно очистить какие-либо ресурсы, которые у вас есть, тогда блок finally - лучший вариант.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...