В каком порядке выполняется метод с try-finally, если в try есть return?
Что проверяют
Знаете ли вы точную последовательность, а не только фразу «finally выполняется всегда». Следом почти наверняка спросят, что вернёт метод, если в finally изменить возвращаемую переменную, и можно ли написать return внутри finally.
Как отвечать
Порядок такой:
- вычисление: выполняется код
try, затем выражение вreturn. Его значение копируется во скрытую локальную переменную; - finally: отрабатывает блок
finally; - возврат: метод отдаёт вызывающему значение, сохранённое на первом шаге.
Отсюда классический вопрос с подвохом:
int x = 1;
try { return x; }
finally { x = 2; }Метод вернёт 1: значение уже скопировано, изменение x в finally на результат не влияет. Со ссылочным типом тоньше. Если в try возвращается list, а в finally вызывается list.Add(...), вызывающий увидит новый элемент: скопирована ссылка, объект общий. А присваивание list = new List<int>() в finally ничего не изменит по той же причине.
Ещё несколько фактов:
returnвfinally: в C# запрещён, ошибка компиляции CS0157, управление не может покинутьfinally. В Java так можно, отсюда частая путаница;- исключение в
finally: заменяет исходное, и первое исключение теряется; - когда
finallyне выполнится:Environment.FailFast, убийство процесса,StackOverflowException. Для необработанного исключения документация прямо говорит, что выполнениеfinallyне гарантировано; using: компилятор разворачивает его вtry-finallyс вызовомDispose().
Где спотыкаются
Говорят, что finally выполняется после возврата из метода. Он выполняется до того, как управление вернётся вызывающему, но уже после вычисления возвращаемого значения.
Уверенно отвечают, что метод вернёт 2, путая переприсваивание переменной с изменением объекта по ссылке.
Утверждают, что finally выполнится в любом случае. Сценарии с FailFast, переполнением стека и необработанным исключением показывают, что гарантия не абсолютная, и для критичной очистки на неё одну полагаться нельзя.