JavaRush /Курси /C# SELF /Блок finally та оператор throw

Блок finally та оператор throw

C# SELF
Рівень 13 , Лекція 2
Відкрита

1. Знайомство з блоком finally

Очищення та звільнення ресурсів

Уявіть: ви експериментуєте у хімічній лабораторії, і коли усе закінчили (або навіть якщо підірвали кілька колбочок), вам усе одно треба прибрати робоче місце, змити хімікати й вимкнути світло. Саме для цього потрібен блок finally — він спрацьовує завжди, після try і catch, незалежно від того, стався виняток чи ні.


try
{
    // Тут пишемо "небезпечний" код, який може спричинити виняток
}
catch
{
    // Тут перехоплюємо та обробляємо виняток
}
finally
{
    // Цей код виконається завжди — був виняток чи ні
}
Структура try-catch-finally у C#

Навіщо це потрібно? Насамперед, щоб гарантовано звільнити ресурси: закрити файл, з’єднання з базою даних, розблокувати двері до серверної… Якби не було finally, після помилки деякі ресурси могли б лишатися «завислими» — а це вже справжня проблема. Наприклад, файл буде заблоковано, і його не зможе відкрити навіть адміністратор.

Чому не можна все писати у catch?

Чи могли б ми просто звільнити ресурс у catch? Теоретично так. Але якщо все минуло добре, catch не виконається. Якщо ми хочемо бути певні, що звільнення ресурсу відбудеться завжди, нам потрібен finally.

У реальному житті часто трапляються такі завдання, де неважливо, був успіх чи провал — прибирання обов’язкове. Саме для цього й придумали finally.

2. Особливості блоку finally

Коли finally не спрацьовує?

Питання з підступом! finally виконується завжди. Навіть якщо в блоці try викликається return (ранній вихід з методу) або генерується новий виняток, finally усе одно буде виконано.


static void Test()
{
    try
    {
        Console.WriteLine("До return");
        return;
    }
    finally
    {
        Console.WriteLine("finally усе одно спрацює!");
    }
}

//Виклик Test()
Test();
Виведе:

// До return
// finally усе одно спрацює!
    

Але якщо раптом із вашим застосунком станеться «жорстке падіння» (наприклад, вимкнули комп’ютер, процес примусово завершили або припинилася робота CLR), блок finally не виконається. Тут уже нічого не вдієш.

Оператор finally і стек викликів

Стек викликів ми детальніше розглянемо на наступній лекції, а зараз коротко: це мовби стос викликаних методів, яким програма «спускається», якщо не знаходить відповідного catch.

Ще один важливий момент: якщо в try виник виняток, а в catch його не перехоплено (наприклад, немає відповідного обробника), програма залишає поточний метод і рухається далі стеком викликів, доки не знайде відповідний catch. Але перед цим обов’язково виконається блок finally на кожному рівні стека.

Це гарантує, що ресурси звільняються коректно, навіть у разі нетипових помилок.

3. Оператор throw: як самостійно кидати винятки

Що таке throw і навіщо він потрібен

Іноді простого перехоплення винятку недостатньо — буває, треба створити власну помилку і «кинути» її назовні. Саме для цього існує оператор throw.


throw new Exception("Це моя спеціальна помилка!");
Створення та кидання власного винятку

throw буквально каже CLR: «Я тут виявив щось дуже погане, кидаю свій Exception, далі нехай розбирається той, хто викликав цей код».

throw без створення нового винятку

Можна використовувати throw; усередині блоку catch, щоб повторно кинути щойно перехоплений виняток — наприклад, якщо ви обробили частину помилки, але далі відповідальність за обробку хочете делегувати вищому рівню коду.


try
{
    DangerousOperation();
}
catch (Exception ex)
{
    LogError(ex);
    throw; // повторно кидає поточний виняток, стек викликів буде збережено
}
Повторне кидання поточного винятку із збереженням стека викликів

Якби ми написали throw ex;, інформацію про стек викликів було б втрачено — це погана практика.

4. Як finally працює разом із throw?

finally спрацьовує навіть під час кидання винятку

Давайте перевіримо, що станеться, якщо всередині try відбувається throw, але в нас є й finally:


try
{
    Console.WriteLine("До помилки...");
    throw new Exception("Помилка під час try!");
}
catch
{
    Console.WriteLine("Catch перехоплює помилку.");
}
finally
{
    Console.WriteLine("Finally спрацював.");
}
finally виконується навіть під час кидання винятку

Результат:


До помилки...
Catch перехоплює помилку.
Finally спрацював.

І якщо немає відповідного catch, finally усе одно відпрацює перед тим, як програма аварійно завершиться.

Нюанси: що буде, якщо у finally теж викликати throw?

Якщо у finally станеться throw, цей виняток замінить попередній. Тобто інформацію про те, що сталося в try/catch, буде втрачено. Тому не рекомендують кидати throw у finally, якщо всередині вже стався виняток.


try
{
    throw new Exception("Помилка у try");
}
finally
{
    throw new Exception("Помилка у finally");
}
// Підсумок: назовні вийде "Помилка у finally"
Виняток із finally замінює попередній

5. Практичні поради та типові помилки

Що частіше забувають новачки

  • Не використовувати блок finally для звільнення ресурсів, покладаючись лише на catch.
  • Розміщувати код, який може спричинити нові винятки, всередині finally — це призводить до неочікуваних помилок.
  • Забувати, що return усередині try не «обходить» finally — він завжди виконається.

Альтернатива finally: що обрати?

З появою конструкції using (ми детально розглянемо її згодом) звільнення ресурсів стало ще зручнішим, але по суті using під капотом використовує той самий finally. Для будь-яких нестандартних ситуацій (наприклад, розблокування чи надсилання повідомлення про помилку) усе одно доводиться використовувати finally.

За що люблять finally на співбесідах

Кожен рекрутер, який хоч раз писав сервер із великим навантаженням, любить ставити запитання про finally. Зазвичай запитують: «Що буде, якщо в try стоїть return, а в finallythrow?» або «Чи гарантоване звільнення ресурсів під час винятків?». І тепер у вас є не просто відповіді, а розуміння. Ви зможете не лише розповісти, як працює finally, а й пояснити, навіщо він потрібен, коли його використовувати і чому без нього жоден серйозний код не обходиться.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ