← Назад к вопросам

Приведи пример использования кастомных исключений

2.0 Middle🔥 192 комментариев
#Основы C# и .NET

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Пример использования кастомных исключений в C#

Кастомные (пользовательские) исключения в C# используются для создания специализированных типов ошибок, которые лучше отражают логику вашего домена приложения. Они позволяют отделить системные исключения (вроде ArgumentException) от бизнес-логических ошибок, делая код более читаемым и предоставляя более точную информацию для обработки.

Основная идея

Создание кастомного исключения обычно включает наследование от базовых классов исключений (Exception, ApplicationException или других специализированных). Это позволяет:

  1. Добавить дополнительные свойства для контекста ошибки.
  2. Определить специфичные сообщения и форматы.
  3. Обеспечить более точную фильтрацию в блоке catch.

Практический пример: система банковских транзакций

Рассмотрим систему, где нужно обрабатывать ошибки, связанные с недостатком средств на счете.

Шаг 1: Создание кастомного исключения

Создадим исключение InsufficientFundsException, которое будет содержать дополнительную информацию о текущем балансе и требуемой сумме.

using System;

namespace BankingApp.Exceptions
{
    /// <summary>
    /// Исключение, возникающее при попытке списать средства, превышающие баланс счета.
    /// </summary>
    public class InsufficientFundsException : Exception
    {
        // Дополнительные свойства для контекста ошибки
        public decimal CurrentBalance { get; }
        public decimal RequiredAmount { get; }
        public string AccountNumber { get; }

        // Конструкторы для различных случаев
        public InsufficientFundsException(decimal currentBalance, decimal requiredAmount)
            : base($"Недостаточно средств. Баланс: {currentBalance}, требуется: {requiredAmount}")
        {
            CurrentBalance = currentBalance;
            RequiredAmount = requiredAmount;
        }

        public InsufficientFundsException(decimal currentBalance, decimal requiredAmount, string accountNumber)
            : base($"Недостаточно средств на счете {accountNumber}. Баланс: {currentBalance}, требуется: {requiredAmount}")
        {
            CurrentBalance = currentBalance;
            RequiredAmount = requiredAmount;
            AccountNumber = accountNumber;
        }

        // Конструктор для передачи внутреннего исключения (для цепочки исключений)
        public InsufficientFundsException(decimal currentBalance, decimal requiredAmount, Exception innerException)
            : base($"Недостаточно средств. Баланс: {currentBalance}, требуется: {requiredAmount}", innerException)
        {
            CurrentBalance = currentBalance;
            RequiredAmount = requiredAmount;
        }
    }
}

Шаг 2: Использование в бизнес-логике

В классе, отвечающем за операции со счетом, мы бросим это исключение при попытке списать больше средств, чем есть на балансе.

using BankingApp.Exceptions;

namespace BankingApp.Services
{
    public class AccountService
    {
        public decimal Withdraw(string accountNumber, decimal amount)
        {
            // Получаем текущий баланс (в реальном приложении - из базы данных)
            decimal currentBalance = GetBalance(accountNumber);

            if (amount > currentBalance)
            {
                // Бросаем кастомное исключение вместо стандартного ArgumentException
                throw new InsufficientFundsException(currentBalance, amount, accountNumber);
            }

            // Выполняем списание
            ExecuteWithdrawal(accountNumber, amount);
            return currentBalance - amount;
        }

        private decimal GetBalance(string accountNumber)
        {
            // Пример реализации
            return 1000.00m; // Фиксированный баланс для примера
        }

        private void ExecuteWithdrawal(string accountNumber, decimal amount)
        {
            // Логика списания
        }
    }
}

Шаг 3: Обработка исключения на верхнем уровне

На уровне контроллера API или UI мы можем обработать это исключение специфическим образом, например, возвращать пользователю детализированное сообщение.

using BankingApp.Exceptions;
using BankingApp.Services;

namespace BankingApp.Controllers
{
    public class TransactionController
    {
        private AccountService _accountService = new AccountService();

        public OperationResult ProcessWithdrawal(string accountNumber, decimal amount)
        {
            try
            {
                decimal newBalance = _accountService.Withdraw(accountNumber, amount);
                return OperationResult.Success($"Списание выполнено. Новый баланс: {newBalance}");
            }
            catch (InsufficientFundsException ex) // Специфичная обработка
            {
                // Логирование деталей
                Console.WriteLine($"Недостаток средств: {ex.AccountNumber}, баланс: {ex.CurrentBalance}");

                // Возврат пользовательского сообщения
                return OperationResult.Failure(ex.Message);
            }
            catch (Exception ex) // Общая обработка других исключений
            {
                return OperationResult.Failure($"Общая ошибка: {ex.Message}");
            }
        }
    }

    public class OperationResult
    {
        public bool IsSuccess { get; set; }
        public string Message { get; set; }

        public static OperationResult Success(string message) => new OperationResult { IsSuccess = true, Message = message };
        public static OperationResult Failure(string message) => new OperationResult { IsSuccess = false, Message = message };
    }
}

Преимущества использования кастомных исключений

  1. Улучшенная семантика: Исключение InsufficientFundsException сразу говорит о причине ошибки, в отличие от общего InvalidOperationException.
  2. Расширенный контекст: Добавление свойств CurrentBalance, RequiredAmount и AccountNumber позволяет передавать дополнительные данные для обработки или логирования.
  3. Точная фильтрация: В блоке catch мы можем точно определить тип ошибки и обработать её соответственно, не смешивая с другими исключениями.
  4. Согласованность в архитектуре: Кастомные исключения становятся частью доменной модели, что упрощает взаимодействие между слоями приложения.
  5. Упрощение тестирования: В unit-тестах можно ожидать конкретное исключение и проверять его свойства.

Рекомендации по созданию кастомных исключений

  • Наследуйтесь от Exception или наиболее подходящего базового исключения (ApplicationException считается менее используемым).
  • Всегда реализуйте стандартные конструкторы (как минимум с сообщением и внутренним исключением).
  • Добавляйте свойства только для действительно полезной контекстной информации.
  • Используйте сериализацию, если исключения могут передаваться между доменами (например, через WCF или границы процессов).
  • Не создавайте слишком много исключений — только для ключевых бизнес-сценариев.

Кастомные исключения — это мощный инструмент для создания чистого, доменно-ориентированного кода, который точно сообщает о проблемах и упрощает их обработку на всех уровнях приложения.