Что такое кастомное исключение?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое кастомное исключение?
Кастомное исключение (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-приложениях.