OOP Course
Сьогодні

Підрозділ 5.4

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

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

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

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

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

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

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

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

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

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

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

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

using System;

RegisterPatient("Іван Петренко", 45);
RegisterPatient("", 30);
RegisterPatient("Марія Сидоренко", -5);

void RegisterPatient(string name, int age)
{
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException("Ім'я пацієнта не може бути порожнім.");

    if (age < 0 || age > 150)
        throw new ArgumentOutOfRangeException(nameof(age),
            $"Вік {age.ToString()} є неприпустимим значенням.");

    Console.WriteLine($"Зареєстровано: {name}, {age.ToString()} р.");
}

Цей код компілюється, але виконати його до кінця не вдасться: вже на другому виклику RegisterPatient("", 30) виняток ArgumentException ніхто не перехоплює, і програма аварійно завершується (Unhandled exception), а третій виклик так і не відбудеться. Загорнемо виклики в try...catch:

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

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

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

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

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

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

throw як вираз і ThrowIfNull

З C# 7 throw може бути не лише інструкцією, а й виразом — у тернарному операторі, після ?? та в тілі-виразі =>. Це дозволяє компактно перевіряти аргументи:

public Patient(string name) =>
    Name = name ?? throw new ArgumentNullException(nameof(name));

Для найчастіших перевірок .NET має готові статичні методи: ArgumentNullException.ThrowIfNull(name) (.NET 6), ArgumentException.ThrowIfNullOrWhiteSpace(name) (.NET 8), ArgumentOutOfRangeException.ThrowIfNegative(age) (.NET 8). Вони самі підставляють ім'я параметра в повідомлення.

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 ·