Какое использование блока finally, которому предшествует блок catch-all catch, в С#?

Рассмотрим следующую структуру кода С# (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.
}

Ответ 1

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

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

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

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

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

if (x) y;

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

if (x) y; z;

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

if (x) 
{
    y;
}

всегда выигрывает для меня.

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

Во втором случае управление будет доступно только S2, если вы проглотите любое исключение, когда-либо попавшее в блокировку catch! Try-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;

    // 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;

Ответ 9

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

Ответ 10

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