Підрозділ 5.4
Генерація винятку та оператор throw
Пояснює ручну генерацію винятків через throw, передачу повідомлення в Exception і повторне викидання винятку з catch.
5.4. Генерація винятку та оператор throw
До цього моменту ми розглядали лише перехоплення винятків, які генерує сама система. Але в C# будь-який метод може генерувати виняток вручну — оператором 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 у методі — це не помилка, а норма. Метод має «кидати» виняток, якщо не може виконати свою роботу коректно. Обробку помилки слід залишати тому рівню, який знає, що з нею робити.