Приведи пример использования кастомных исключений
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Пример использования кастомных исключений в C#
Кастомные (пользовательские) исключения в C# используются для создания специализированных типов ошибок, которые лучше отражают логику вашего домена приложения. Они позволяют отделить системные исключения (вроде ArgumentException) от бизнес-логических ошибок, делая код более читаемым и предоставляя более точную информацию для обработки.
Основная идея
Создание кастомного исключения обычно включает наследование от базовых классов исключений (Exception, ApplicationException или других специализированных). Это позволяет:
- Добавить дополнительные свойства для контекста ошибки.
- Определить специфичные сообщения и форматы.
- Обеспечить более точную фильтрацию в блоке
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 };
}
}
Преимущества использования кастомных исключений
- Улучшенная семантика: Исключение
InsufficientFundsExceptionсразу говорит о причине ошибки, в отличие от общегоInvalidOperationException. - Расширенный контекст: Добавление свойств
CurrentBalance,RequiredAmountиAccountNumberпозволяет передавать дополнительные данные для обработки или логирования. - Точная фильтрация: В блоке
catchмы можем точно определить тип ошибки и обработать её соответственно, не смешивая с другими исключениями. - Согласованность в архитектуре: Кастомные исключения становятся частью доменной модели, что упрощает взаимодействие между слоями приложения.
- Упрощение тестирования: В unit-тестах можно ожидать конкретное исключение и проверять его свойства.
Рекомендации по созданию кастомных исключений
- Наследуйтесь от
Exceptionили наиболее подходящего базового исключения (ApplicationExceptionсчитается менее используемым). - Всегда реализуйте стандартные конструкторы (как минимум с сообщением и внутренним исключением).
- Добавляйте свойства только для действительно полезной контекстной информации.
- Используйте сериализацию, если исключения могут передаваться между доменами (например, через WCF или границы процессов).
- Не создавайте слишком много исключений — только для ключевых бизнес-сценариев.
Кастомные исключения — это мощный инструмент для создания чистого, доменно-ориентированного кода, который точно сообщает о проблемах и упрощает их обработку на всех уровнях приложения.