Есть какие-нибудь мысли по поводу практики в целом?
Да. Будьте осторожны .Когда ваш блок finally работает, вполне возможно, что он работает, потому что было обработано неожиданное исключение .Это означает, что что-то сломано , и может произойти что-то совершенно неожиданное .
В этой ситуации возможно, что вам вообще не следует запускать код в блоках finally.Код в блоке finally может быть построен так, чтобы предполагать, что подсистемы, от которых он зависит, работоспособны, хотя на самом деле они могут быть сильно повреждены.Код в блоке finally может ухудшить ситуацию.
Например, я часто вижу подобные вещи:
DisableAccessToTheResource();
try
{
DoSomethingToTheResource();
}
finally
{
EnableAccessToTheResource();
}
Автор этого кода думает: «Я делаю временную мутацию в состоянии мира; мне нужновосстановить состояние до того, что было до того, как меня вызвали ».Но давайте подумаем о том, как все может пойти не так.
Во-первых, вызывающий уже может отключить доступ к ресурсу;в этом случае этот код повторно разрешает его, возможно, преждевременно.
Во-вторых, если DoSomethingToTheResource выдает исключение, то правильно ли делать доступ к ресурсу ???Код, управляющий ресурсом, неожиданно нарушен .Этот код говорит, по сути, «если код управления нарушен, убедитесь, что другой код может вызвать этот поврежденный код как можно скорее, чтобы он тоже мог ужасно сбоить ».Это кажется плохой идеей.
В-третьих, если DoSomethingToTheResource выдает исключение, то как мы узнаем, что EnableAccessToTheResource также не сгенерирует исключение?Что бы ни случилось, использование ресурса может также повлиять на код очистки, и в этом случае исходное исключение будет потеряно, и проблему будет сложнее диагностировать.
Я склонен писать подобный код без использования try-наконец блокирует:
bool wasDisabled = IsAccessDisabled();
if (!wasDisabled)
DisableAccessToTheResource();
DoSomethingToTheResource();
if (!wasDisabled)
EnableAccessToTheResource();
Теперь состояние не видоизменяется, если только оно не требуется.Теперь состояние вызывающего не перепутано с.И теперь, если DoSomethingToTheResource не удается, то мы не возобновляем доступ.Мы предполагаем, что что-то глубоко сломано и не рискуем усугубить ситуацию, пытаясь продолжить выполнение кода.Пусть звонящий справится с проблемой, если сможет.
Итак, когда стоит запустить блок finally?Во-первых, когда ожидается исключение.Например, вы можете ожидать, что попытка заблокировать файл может быть неудачной, потому что кто-то другой заблокировал его.В этом случае имеет смысл перехватить исключение и сообщить о нем пользователю.В этом случае неопределенность в отношении того, что нарушено, уменьшается;вы вряд ли усугубите ситуацию очисткой.
Во-вторых, когда очищаемый ресурс является дефицитным системным ресурсом.Например, имеет смысл закрыть дескриптор файла в блоке finally.(«Использование» - это, конечно, еще один способ написания блока try-finally.) Содержимое файла может быть повреждено, но сейчас вы ничего не можете с этим поделать.Дескриптор файла в конце концов будет закрыт, так что это может быть скорее раньше, чем позже.