Функциональное Программирование в C#. Продолжение
Начало
2. Ошибки как значения
Обработка ошибок в C# часто выглядит так:
public class UserService
{
public User CreateUser(string email, string password)
{
if (string.IsNullOrEmpty(email))
throw new ArgumentException("Email is required")
if (password.Length < 8)
throw new ArgumentException("Password too short");
if (_userRepo.EmailExists(email))
throw new DuplicateEmailException(email);
// Create user...
}
}
Проблема в том, что:
- исключения дорогие,
- вызывающий код часто забывает обрабатывать исключения,
- сигнатура метода лжёт — она утверждает, что возвращает User, но может выдать исключение.
Мы можем сделать ошибки явными, используя библиотеку OneOf. Она предоставляет дискриминируемые объединения для C#, используя пользовательский тип OneOf<T0, ... Tn>.
public class UserService
{
public OneOf<
User,
ValidationError,
DuplicateEmailError>
CreateUser(string email, string password)
{
if (string.IsNullOrEmpty(email))
return new ValidationError("Email обязателен");
if (password.Length < 8)
return new ValidationError("Пароль короткий");
if (_userRepo.EmailExists(email))
return new DuplicateEmailError(email);
return new User(email, password);
}
}
Сделав ошибки явными, мы добились, что:
- сигнатура метода говорит всю правду,
- вызывающий код должен обработать все возможные результаты,
- не возникает накладных расходов из-за исключений,
- проще отслеживать поток исполнения.
Вот как это использовать:
var result = userService.CreateUser(email, password);
result.Switch(
user => SendWelcomeEmail(user),
valError => HandleError(valError),
dupError => HandleError(dupError)
);
Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/functional-programming-in-csharp-the-practical-parts