Ответ
Пользовательское исключение — это класс, унаследованный от System.Exception или его производных, предназначенный для представления специфичных для предметной области ошибок.
Зачем создавать свои исключения?
- Семантика и ясность:
throw new OrderNotFoundException(...)понятнее, чемthrow new KeyNotFoundException(...). - Группировка обработки: Можно ловить конкретный тип исключения (
catch (PaymentFailedException ex)). - Дополнительные данные: Возможность добавить свойства с контекстом ошибки (ID заказа, код ошибки API).
Правила создания:
- Наследоваться от
Exception(илиApplicationException, но это менее распространено). - Добавлять стандартные конструкторы.
- Помечать класс атрибутом
[Serializable], если исключение может пересекать границы доменов приложения (например, в Remoting или WCF).
Пример правильно оформленного кастомного исключения:
[Serializable] // Для поддержки сериализации
public class InsufficientFundsException : Exception
{
// Дополнительные свойства для контекста
public string AccountId { get; }
public decimal CurrentBalance { get; }
public decimal RequiredAmount { get; }
// Стандартные конструкторы
public InsufficientFundsException() { }
public InsufficientFundsException(string message) : base(message) { }
public InsufficientFundsException(string message, Exception inner) : base(message, inner) { }
// Конструктор с дополнительными данными
public InsufficientFundsException(string accountId, decimal currentBalance, decimal requiredAmount)
: base($"Account '{accountId}' has insufficient funds. Current: {currentBalance}, Required: {requiredAmount}")
{
AccountId = accountId;
CurrentBalance = currentBalance;
RequiredAmount = requiredAmount;
}
// Конструктор для десериализации (важно для [Serializable])
protected InsufficientFundsException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context)
{
AccountId = info.GetString(nameof(AccountId));
CurrentBalance = info.GetDecimal(nameof(CurrentBalance));
RequiredAmount = info.GetDecimal(nameof(RequiredAmount));
}
}
// Использование в бизнес-логике
public void ProcessWithdrawal(string accountId, decimal amount)
{
var balance = GetBalance(accountId);
if (balance < amount)
{
// Бросаем информативное исключение с контекстом
throw new InsufficientFundsException(accountId, balance, amount);
}
// ... продолжение обработки
}
Рекомендация: Не создавайте новые исключения для ситуаций, которые уже точно описываются встроенными типами (ArgumentNullException, InvalidOperationException).