Когда срабатывает блок finally в .NET
Что проверяют
Знаете ли вы, что finally выполняется при любом выходе из try, и главное, в каких случаях он не выполняется. Следом обычно спрашивают, что вернёт метод, если в finally изменить возвращаемую переменную.
Как отвечать
Блок срабатывает, когда управление покидает try или catch любым путём:
- обычное завершение: блок
tryдошёл до конца; - переходы:
return,break,continue,goto. Выражение вreturnвычисляется доfinally, поэтому присваивание локальной переменной вfinallyрезультат не изменит. Изменение полей объекта ссылочного типа при этом будет видно; - исключение: поймано здесь же или выше по стеку. Сначала среда ищет подходящий обработчик, и фильтры
catch (...) when (...)выполняются на этом проходе, раньше вложенныхfinally. Затем стек раскручивается, иfinallyсрабатывают по пути к обработчику.
Когда finally не выполнится:
- необработанное исключение: документация прямо говорит, что гарантии нет, всё зависит от того, как среда завершает процесс;
- аварийное завершение:
Environment.FailFast, убийство процесса извне,StackOverflowException, который в .NET нельзя поймать; - зависание: бесконечный цикл или взаимоблокировка внутри
try; - итератор с
yield:finallyсработает приDisposeперечислителя.foreachего вызывает, а ручнойMoveNextбезDisposeоставит блок невыполненным; async-метод:finallyвыполнится после возобновления, когда ожидаемая задача завершится, возможно на другом потоке. Если задача не завершится никогда, не выполнится и он.
Полезно связать ответ с using: компилятор разворачивает его в try/finally с вызовом Dispose, поэтому для ресурсов обычно пишут using, а не ручной finally.
Где спотыкаются
Говорят «finally выполняется всегда». Без оговорок про необработанные исключения, FailFast и переполнение стека ответ считается неполным.
Бросают исключение внутри finally. Оно заменяет исходное, и настоящая причина сбоя пропадает из логов.
Пытаются написать return в finally. В C# это ошибка компиляции: управление не может покинуть тело finally.