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