OOP Course
Сьогодні

Підрозділ 5.4

Генерація винятку та оператор throw

Пояснює ручну генерацію винятків через throw, передачу повідомлення в Exception і повторне викидання винятку з catch.

5.4. Генерація винятку та оператор throw

До цього моменту ми розглядали лише перехоплення винятків, які генерує сама система. Але в C# будь-який метод може генерувати виняток вручну — оператором throw. Це ключовий інструмент для впровадження бізнес-правил і валідації даних.

Навіщо генерувати виняток вручну

Іноді дані технічно коректні (рядок не порожній, число у діапазоні), але порушують бізнес-правило. Наприклад:

  • вік пацієнта не може бути від'ємним
  • діагноз не може бути порожнім рядком
  • ідентифікатор картки повинен починатися з певного префікса

У таких випадках система не знає, що дані некоректні — це знаємо лише ми. Оператор throw дозволяє сигналізувати про помилку стандартним механізмом винятків.

Дві форми оператора throw та ланцюжок винятків

Форма 1: throw new — генерація нового винятку

throw new ExceptionType("повідомлення про помилку");

Після throw вказується новий об'єкт будь-якого класу винятку. Рядок у конструкторі стає значенням властивості Message.

Цей код не компілюється у повноцінний runnable без try/catch навколо. Загорнемо виклики:

Зверніть увагу: throw у методі RegisterPatient не потребує try...catch всередині нього. Виняток «спливає» вгору до місця виклику, де і перехоплюється.

Throw у методах: валідація аргументів

Найпоширеніший патерн — перевіряти аргументи на початку методу і кидати ArgumentException або ArgumentNullException при порушенні:

Форма 2: throw без аргументів — перекидання винятку

Оператор throw без об'єкта можна використовувати лише всередині блоку catch. Він повторно кидає той самий виняток, що перехоплено — без змін і зі збереженням оригінального стека викликів:

Внутрішній catch логує помилку, але не пригнічує її — throw; передає виняток зовнішньому блоку. Стек викликів залишається незмінним: зовнішній обробник бачить оригінальне місце виникнення помилки.

throw; vs throw ex; — важлива різниця

// Правильно — зберігає оригінальний StackTrace:
catch (Exception ex)
{
    Log(ex);
    throw;
}

// Небезпечно — скидає StackTrace до цього рядка:
catch (Exception ex)
{
    Log(ex);
    throw ex;   // StackTrace тепер вказує на цей рядок, а не на першопричину
}

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

Ланцюжок винятків через InnerException

Іноді доцільно «загорнути» низькорівневий виняток у вищорівневий — зі збереженням оригіналу як InnerException. Це дозволяє надати більш змістовний контекст, не втрачаючи першопричину:

Конструктор більшості класів винятків приймає другим параметром innerException. Завдяки цьому зовнішній код може перевірити ex.InnerException і отримати повний ланцюжок причин.

Підсумок

Форма Де використовується Що робить
throw new Ex("msg") Будь-де Генерує новий виняток
throw new Ex("msg", innerEx) Будь-де Генерує виняток із збереженням причини
throw; Лише у catch Перекидає поточний виняток, зберігаючи StackTrace
throw ex; Лише у catch Перекидає з новим StackTrace — уникайте

throw у методі — це не помилка, а норма. Метод має «кидати» виняток, якщо не може виконати свою роботу коректно. Обробку помилки слід залишати тому рівню, який знає, що з нею робити.

Розроблено Tomka Yurii · © 2026 ·