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

Что такое кастомное исключение?

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

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

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

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

Что такое кастомное исключение?

Кастомное исключение (Custom Exception) — это исключительная ситуация, определяемая разработчиком для отражения специфичных для предметной области ошибок, которые не покрываются стандартными исключениями .NET Framework. Создание собственных типов исключений позволяет повысить читаемость кода, улучшить диагностику ошибок и обеспечить более точную обработку исключительных сценариев в приложении.

Зачем нужны кастомные исключения?

Стандартные исключения .NET, такие как InvalidOperationException или ArgumentException, часто слишком общие. Кастомные исключения решают следующие задачи:

  • Семантическая ясность: Имя исключения точно описывает проблему (например, InsufficientFundsException вместо InvalidOperationException).
  • Дополнительная информация: Возможность добавить специфичные свойства (например, CurrentBalance, RequiredAmount).
  • Контролируемая обработка: Позволяют точнее ловить и обрабатывать конкретные типы ошибок на разных уровнях приложения.
  • Архитектурная целостность: Отражение ошибок предметной области в слоях приложения.

Как создать кастомное исключение?

В C# кастомное исключение — это класс, наследующий от System.Exception или его производных. Рекомендуется следовать конвенциям .NET:

Базовый пример

using System;

namespace MyApp.Exceptions
{
    // Наследуемся от Exception
    public class InvalidOrderException : Exception
    {
        // Конструктор по умолчанию
        public InvalidOrderException() { }

        // Конструктор с сообщением
        public InvalidOrderException(string message) 
            : base(message) { }

        // Конструктор с сообщением и внутренним исключением
        public InvalidOrderException(string message, Exception innerException) 
            : base(message, innerException) { }
    }
}

Расширенный пример с дополнительными свойствами

public class PaymentProcessingException : Exception
{
    // Дополнительные свойства для диагностики
    public string TransactionId { get; }
    public decimal Amount { get; }
    public string PaymentMethod { get; }

    // Конструкторы
    public PaymentProcessingException(string transactionId, decimal amount, 
                                     string paymentMethod)
        : this(transactionId, amount, paymentMethod, 
               $"Payment failed for transaction {transactionId}")
    {
    }

    public PaymentProcessingException(string transactionId, decimal amount,
                                     string paymentMethod, string message)
        : base(message)
    {
        TransactionId = transactionId;
        Amount = amount;
        PaymentMethod = paymentMethod;
    }

    public PaymentProcessingException(string transactionId, decimal amount,
                                     string paymentMethod, string message, 
                                     Exception innerException)
        : base(message, innerException)
    {
        TransactionId = transactionId;
        Amount = amount;
        PaymentMethod = paymentMethod;
    }

    // Переопределение Message для включения дополнительных данных
    public override string Message => 
        $"{base.Message} [Transaction: {TransactionId}, " +
        $"Amount: {Amount}, Method: {PaymentMethod}]";
}

Рекомендации по использованию

1. Наследование

  • Наследуйтесь от Exception, ApplicationException (хотя Microsoft теперь не рекомендует) или более конкретных исключений вроде ArgumentException.
  • Для ошибок валидации можно наследовать от ValidationException.

2. Сериализация

Для корректной работы в распределенных системах и при сериализации:

[Serializable]
public class DomainException : Exception
{
    public DomainException() { }
    public DomainException(string message) : base(message) { }
    public DomainException(string message, Exception inner) 
        : base(message, inner) { }
    
    // Конструктор для десериализации
    protected DomainException(
        System.Runtime.Serialization.SerializationInfo info,
        System.Runtime.Serialization.StreamingContext context)
        : base(info, context) { }
}

3. Именование

  • Используйте суффикс Exception
  • Называйте исключения согласно их назначению: UserNotFoundException, InvalidConfigurationException

4. Группировка исключений

Создавайте иерархии исключений для сложных систем:

// Базовое исключение для домена
public abstract class DomainException : Exception { }

// Конкретные исключения
public class ValidationException : DomainException { }
public class BusinessRuleException : DomainException { }
public class InfrastructureException : DomainException { }

Пример использования в реальном сценарии

public class BankAccount
{
    private decimal _balance;

    public void Withdraw(decimal amount)
    {
        if (amount <= 0)
            throw new ArgumentException("Amount must be positive", nameof(amount));
        
        if (amount > _balance)
            throw new InsufficientFundsException(_balance, amount);
        
        _balance -= amount;
    }
}

// Где-то в коде обработки
try
{
    account.Withdraw(1000);
}
catch (InsufficientFundsException ex)
{
    // Специфичная обработка
    logger.LogWarning($"Insufficient funds: {ex.CurrentBalance} < {ex.RequiredAmount}");
    throw new PaymentDeclinedException("Insufficient funds", ex);
}
catch (Exception ex)
{
    // Общая обработка
    logger.LogError(ex, "Withdrawal failed");
    throw;
}

Лучшие практики

  • Не создавайте исключения для контроля потока выполнения — используйте их только для исключительных ситуаций.
  • Добавляйте информативные сообщения, которые помогут в диагностике.
  • Логируйте исключения на соответствующем уровне абстракции.
  • Не раскрывайте чувствительную информацию в сообщениях исключений.
  • Документируйте кастомные исключения в XML-комментариях.

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