如何在不抛异常时退出调用方法?C#守卫子句优化
在C#中实现无判断的Result式流程控制
我在C#开发中采用Result对象替代异常,通过Web管道过滤器将Result映射为HTTP响应码。现有带守卫子句的代码包含多次仓储调用,虽已做初步重构,但仍需通过判断才能退出方法。我希望Guard(id)或LoadDto3(id)在出错时直接退出Method1,让公共方法只保留核心业务调用,方便业务人员理解流程,该怎么实现?
原代码示例
public Result Method1(string id) { if (string.IsNullOrWhiteSpace(id)) return ArgumentError(nameof(id)); var dto1 = _repository1.Get(id); if (dto1 == null) return NotFoundError(); var dto2 = _repository2.Get(dto1.Dto2Id); if (dto2 == null) return NotFoundError(); var dto3 = _repository3.Get(dto2.Dto3Id); if (dto3 == null) return NotFoundError(); Handle(dto3); }
重构后代码
public Result Method1(string id) { var guardResult = Guard(id); if (guardResult.IsFailed) return guardResult; var dto3 = LoadDto3(id); if (dto3 == null) return NotFoundError(); Handle(dto3); }
期望代码形式
public Result Method1(string id) { Guard(id); var dto3 = LoadDto3(id); Handle(dto3); }
实现方案
方案1:链式调用封装(最推荐)
通过定义通用Result<T>类型和扩展方法,把"失败即返回"的逻辑封装起来,让业务方法只保留核心流程调用。
1. 定义基础Result类型
// 错误信息载体 public record Error(string Code, string Message); // 基础Result类 public class Result { public bool IsSuccess { get; } public bool IsFailed => !IsSuccess; public Error Error { get; } protected Result(bool isSuccess) { IsSuccess = isSuccess; Error = default; } protected Result(Error error) { IsSuccess = false; Error = error; } public static Result Success() => new Result(true); public static Result Failure(Error error) => new Result(error); } // 带返回值的Result类 public class Result<T> : Result { public T Value { get; } private Result(T value) : base(true) => Value = value; private Result(Error error) : base(error) => Value = default; public static new Result<T> Success(T value) => new Result<T>(value); public static new Result<T> Failure(Error error) => new Result<T>(error); }
2. 编写Result扩展方法
public static class ResultExtensions { // 无返回值Result:失败则直接返回,成功则执行后续操作 public static Result Then(this Result result, Func<Result> action) { if (result.IsFailed) return result; return action(); } // 带返回值Result:失败则直接返回,成功则将值传入后续操作 public static Result Then<T>(this Result<T> result, Func<T, Result> action) { if (result.IsFailed) return result; return action(result.Value); } }
3. 修改业务方法实现
调整Guard和LoadDto3的返回类型,然后用链式调用重构Method1:
public Result Method1(string id) { return Guard(id) .Then(() => LoadDto3(id)) .Then(dto3 => Handle(dto3)); } // 守卫方法:返回Result,失败时携带错误信息 private Result Guard(string id) { if (string.IsNullOrWhiteSpace(id)) return Result.Failure(new Error("InvalidArgument", $"参数 {nameof(id)} 不能为空")); return Result.Success(); } // 加载Dto3的方法:返回Result<Dto3>,失败时返回NotFound错误 private Result<Dto3> LoadDto3(string id) { var dto1 = _repository1.Get(id); if (dto1 == null) return Result<Dto3>.Failure(new Error("NotFound", "未找到Dto1")); var dto2 = _repository2.Get(dto1.Dto2Id); if (dto2 == null) return Result<Dto3>.Failure(new Error("NotFound", "未找到Dto2")); var dto3 = _repository3.Get(dto2.Dto3Id); if (dto3 == null) return Result<Dto3>.Failure(new Error("NotFound", "未找到Dto3")); return Result<Dto3>.Success(dto3); } // 业务处理方法:返回Result private Result Handle(Dto3 dto3) { // 核心业务逻辑 return Result.Success(); }
这种方式完全消除了判断语句,Method1只保留核心流程的调用链,业务人员能快速理解整个流程的执行顺序。
方案2:局部辅助方法简化判断
如果不想用链式调用,可以封装一个局部辅助方法来减少重复判断:
public Result Method1(string id) { // 局部方法:检查Result是否失败,失败则直接返回 Result CheckFail(Result result) => result.IsFailed ? result : null; var guardFail = CheckFail(Guard(id)); if (guardFail != null) return guardFail; var dto3Result = LoadDto3(id); var loadFail = CheckFail(dto3Result); if (loadFail != null) return loadFail; return Handle(dto3Result.Value); }
这种方式虽然仍有少量判断,但比原代码简洁很多,且逻辑复用性更高。
内容的提问来源于stack exchange,提问作者fibach
相关产品推荐
相关产品推荐

