异常是控制流程中的传统做法。
如果出现错误,创建并抛出一个异常。
把这个剩下的交给高级函数。
这种方法虽然有效,但也存在一些缺点。
高层函数最好知道底层函数的工作原理。
当你不停地加入特例时,代码就会变得杂乱冗长。
实际上,使用异常会增加成本并影响性能。
连调试都更难了。
public double GetCustomerDiscount(Guid customerId, double price)
{
try
{
var customer = GetCustomer(customerId);
return customer.DiscountRate * price;
}
catch (CustomerNotFoundException ex)
{
// 处理一下异常
}
catch (Exception ex)
{
// 进行一些处理
}
}
public Customer 根据ID查找客户(Guid 客户ID)
{
try
{
var 客户 = context.Customers.FirstOrDefault(c => c.CustomerId == 客户ID);
if (客户 is null)
{
return new CustomerNotFoundException();
}
return 客户;
}
catch(Exception ex)
{
logger.LogError(ex.Message.ToString());
throw;
}
}
正如你所见,高层函数才能更好地了解底层实现的细节。
public double GetCustomerDiscount(Guid customerId, Guid productId)
{
try
{
var customer = GetCustomer(customerId);
var product = GetProduct(productId);
return customer.折扣率 * product.Price;
}
catch (CustomerNotFoundException ex)
{
// 处理异常
}
catch (ProductNotFoundException ex)
{
// 处理特殊情况
}
catch (Exception ex)
{
// 处理异常
}
}
public Product GetProduct(Guid productId)
{
try
{
var product = context.Products.FirstOrDefault(p => p.ProductId == productId);
if (product == null)
{
return new ProductNotFoundException();
}
return product;
}
catch (Exception ex)
{
logger.LogError(ex.Message.ToString());
throw;
}
}
稍微改一下代码,你会发现它变长且更难打理了。
幸運的是,我們有一個替代的控制流方法,叫做結果匹配模式。
public abstract record 结果模型(string 信息);
public record 错误(string 信息) : 结果模型(信息);
public record 警告(string 信息) : 结果模型(信息);
public class Result
{
public bool Is成功 => Errors.Count == 0;
public bool Is失败 => !Is成功;
public List<Error> Errors { get; protected set; } = [];
public List<Warning> Warnings { get; protected set; } = [];
protected Result(List<Error> errors, List<Warning> warnings)
{
Errors = errors;
Warnings = warnings;
}
public static Result Ok()
{
return new Result([], []);
}
public static Result Fail(params List<Error> errors)
{
var validErrors = errors.Where(e => e is not null).ToList();
如果有效错误列表为空
{
validErrors.Add(new Error("没有指定任何错误信息的失败结果被创建"));
}
return new Result(validErrors, []);
}
public static implicit operator Result(Error error)
{
return Fail(error);
}
public static implicit operator Result(List<Error> errors)
{
return Fail(errors);
}
public void Add(ResultBaseModel model)
{
如果模型是错误对象
{
Errors.Add((model as Error)!);
return;
}
如果模型是警告对象
{
Warnings.Add((model as Warning)!);
return;
}
}
public void AddRange(IReadOnlyList<ResultBaseModel> models)
{
foreach (var model in models)
{
Add(model);
}
}
}
public sealed class Result<T> : Result where T : notnull
{
public T? Payload { get; } = default;
private Result(T payload) : base([], [])
{
Payload = payload;
}
private Result(List<Error> 错误, List<Warning> 警告) : base(错误, 警告)
{
}
public static Result<T> Ok(T payload)
{
return new Result<T>(payload);
}
public static new Result<T> Fail(params List<Error> errors)
{
var 有效错误 = errors.Where(e => e 不是 null).ToList();
if (有效错误.Count == 0)
{
有效错误.Add(new Error("失败结果是在没有任何错误的情况下创建的."));
}
return new Result<T>(有效错误, []);
}
public static implicit operator Result<T>(T payload)
{
return new Result<T>(payload);
}
public static implicit operator Result<T>(Error error)
{
return Result<T>.Fail(error);
}
public static implicit operator Result<T>(List<Error> errors)
{
return Result<T>.Fail(errors);
}
}
还有额外的:
public static void Merge(this Result result, params Result[] results)
{
var errors = results.SelectMany(r => r.Errors).ToList();
result.AddRange(errors);
var warnings = results.SelectMany(r => r.Warnings).ToList();
result.AddRange(warnings);
}
public static void MergeErrorsAsWarnings(this Result result, params Result[] results)
{
var errors = results.SelectMany(r => r.Errors).ToList();
result.AddRange(errors.Select(x => new Warning(x.Message)).ToList());
var warnings = results.SelectMany(r => r.Warnings).ToList();
result.AddRange(warnings);
}
public static Result<T> ToResult<T>(this Result result) where T : notnull
{
var resultT = Result<T>.Fail([.. result.Errors]);
resultT.AddRange(result.Warnings);
return resultT;
}
我们现在准备好使用结果模式(Result Pattern)了。
public Result<double> 获取客户折扣率(Guid 客户ID, Guid 产品ID)
{
var 客户结果 = 获取客户(客户ID);
if (客户结果.IsFailed)
{
return 客户结果.Errors;
}
var 产品结果 = 获取产品(产品ID);
if (产品结果.IsFailed)
{
return 产品结果.Errors;
}
var 折扣率 = 客户结果.Payload!.DiscountRate;
var 价格 = 产品结果.Payload!.Price;
return 折扣率 * 价格; // 返回折扣后的价格
}
public Result<Customer> GetCustomer(Guid 客户Id)
{
try
{
var 客户 = context.Customers.FirstOrDefault(c => c.CustomerId == 客户Id);
if (客户 is null)
{
return new Error($"未找到ID为'{客户Id}'的客户。");
}
return 客户;
}
catch(Exception ex)
{
return new Error($"获取ID为'{客户Id}'的客户时出错。错误消息: '{ex}'.");
}
}
public Result<Product> GetProduct(Guid productId)
{
try
{
var product = context.Products.FirstOrDefault(p => p.ProductId == productId);
if (product is null)
{
return new Error($"找不到该ID为'{productId}'的产品。");
}
return product;
}
catch(Exception ex)
{
return new Error($"获取ID为'{productId}'的产品时出错。错误信息:'{ex.Message}'.");
}
}
如果有任何疑问,请在下方留言。
不必复制代码。
代码库地址在这里:https://github.com/dcyuksel/Result
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦