Ответ
Блок finally используется в конструкции try-catch-finally для гарантированного выполнения кода очистки или финализации независимо от того, как был завершён блок try — нормально, через исключение или даже через оператор return.
Основное назначение — освобождение неуправляемых или критических ресурсов: закрытие файлов, сетевых соединений, графических дескрипторов, чтобы избежать утечек.
Пример (паттерн до C# 8.0):
FileStream fileStream = null;
try
{
fileStream = File.OpenRead("data.bin");
// Работа с файлом, которая может вызвать IOException
var data = new byte[100];
fileStream.Read(data, 0, 100);
// Даже return здесь не отменит finally!
// return;
}
catch (IOException ex)
{
Console.WriteLine($"Ошибка чтения файла: {ex.Message}");
// Исключение будет проброшено ДАЛЬШЕ, но finally выполнится ПРЕЖДЕ.
throw;
}
finally
{
// Этот код выполнится ВСЕГДА:
// 1. После успешного завершения try.
// 2. После обработки исключения в catch.
// 3. Даже если исключение не было перехвачено (после catch).
// 4. При выходе из try через return, break, continue.
fileStream?.Dispose();
Console.WriteLine("Ресурс файла освобождён.");
}
Современная альтернатива (using statement):
Для объектов, реализующих IDisposable, блок finally для вызова .Dispose() обычно инкапсулируется в оператор using, что является синтаксическим сахаром для той же конструкции try-finally.
using (var fileStream = File.OpenRead("data.bin"))
using (var reader = new StreamReader(fileStream))
{
// Работа с reader
} // Dispose() будет вызван автоматически здесь, в неявном блоке finally
Важно: finally не выполнится только в исключительных случаях: аварийное завершение процесса (Environment.FailFast), фатальное исключение (например, StackOverflowException, OutOfMemoryException в некоторых ситуациях) или бесконечный цикл в блоке try.