Что пользы от Блока finally, которому предшествует блок catch-all catch в C#?

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

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

S3;

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

вопрос

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

пример

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 ответов


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

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

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

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


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

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


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


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

с уважением,

Себастиан


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

В то время как вы могли бы поспорить, что

if (x) y;

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

if (x) y; z;

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

if (x) 
{
    y;
}

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


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

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


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

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


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

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


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