Будет ли Try/finally (без Catch) пузырьком исключение?

Я почти уверен, что ответ ДА. Если я использую блок Try finally, но не использую блок Catch, тогда любые исключения будут пузыряться. Правильно?

Любые мысли о практике в целом?

Сет

Ответ 1

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

Ответ 2

Любые мысли о практике в целом?

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

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

Например, я часто вижу такие вещи:

DisableAccessToTheResource();
try
{
    DoSomethingToTheResource();
}
finally
{
    EnableAccessToTheResource();
}

Автор этого кода думает: "Я делаю временную мутацию для состояния мира, мне нужно восстановить состояние до того, что было до того, как я был вызван". Но подумайте о том, как это может пойти не так.

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

Во-вторых, если DoSomethingToTheResource выбрасывает исключение, правильно ли это сделать, чтобы разрешить доступ к ресурсу??? Код, управляющий ресурсом, неожиданно нарушен. В этом коде говорится, что "если код управления нарушен, убедитесь, что другой код может как можно скорее вызвать этот сломанный код, чтобы он тоже мог терпеть неудачу". Это кажется плохой идеей.

В-третьих, если DoSomethingToTheResource генерирует исключение, то откуда мы знаем, что EnableAccessToTheResource также не будет генерировать исключение? Какая бы ужасность не испортила использование ресурса, также может повлиять на код очистки, и в этом случае исходное исключение будет потеряно, и проблема будет сложнее диагностировать.

Я стараюсь писать такой код без использования блоков try-finally:

bool wasDisabled = IsAccessDisabled();
if (!wasDisabled)
    DisableAccessToTheResource();
DoSomethingToTheResource();
if (!wasDisabled)
    EnableAccessToTheResource();

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

Итак, когда неплохо запустить блок finally? Во-первых, когда ожидается исключение. Например, вы можете ожидать, что попытка заблокировать файл может завершиться неудачей, поскольку кто-то еще заблокировал его. В этом случае имеет смысл поймать исключение и сообщить об этом пользователю. В этом случае неопределенность в отношении того, что разрушена, уменьшается; вы вряд ли усугубите ситуацию, очистив.

Во-вторых, когда ресурс, который вы очищаете, является скудным системным ресурсом. Например, имеет смысл закрыть дескриптор файла в блоке finally. ( "Использование" - это, конечно, еще один способ написания блока try-finally.) Содержимое файла может быть повреждено, но теперь вы ничего не можете с этим поделать. В конце концов дескриптор файла будет закрыт, поэтому он может быть скорее раньше, чем позже.