C#函数式编程:如何将命令式业务逻辑转为函数式实现?
解决链式调用中保留中间值的几种方案
针对你需要在链式调用里保留中间计算结果并最终返回包含这些值的对象的问题,这里有几种实用的实现方式:
方案1:自定义上下文类承载中间状态
这种方式最适合复杂业务场景,能清晰管理所有中间值,还能方便复用公共计算逻辑。
首先定义对应业务逻辑的上下文类,把需要保留的中间值都作为属性:
// 针对Logic1的上下文,可根据业务扩展或抽象成通用基类 public class Logic1Context { public int B { get; set; } public int C { get; set; } public int D { get; set; } } // 扩展方法实现链式调用 public static class Logic1Extensions { public static Logic1Context ComputeByA(this int a) { return new Logic1Context { B = ComputeByA(a) }; // 调用原ComputeByA方法 } public static Logic1Context ComputeByB(this Logic1Context context) { context.C = ComputeByB(context.B); return context; } public static Logic1Context ComputeByC(this Logic1Context context) { context.D = ComputeByC(context.C); return context; } public static Result1 ToResult(this Logic1Context context) { return new Result1 { B = context.B, C = context.C, D = context.D }; } }
然后你的Logic1就可以写成:
public Result1 Logic1(int a) { return a.ComputeByA() .ComputeByB() .ComputeByC() .ToResult(); }
如果是Logic2,可以单独定义Logic2Context,复用ComputeByA的逻辑:
public class Logic2Context { public int B { get; set; } public int E { get; set; } public int F { get; set; } } public static class Logic2Extensions { public static Logic2Context ComputeByA(this int a) { return new Logic2Context { B = ComputeByA(a) }; } public static Logic2Context ComputeByB2(this Logic2Context context) { context.E = ComputeByB2(context.B); return context; } public static Logic2Context ComputeByE(this Logic2Context context) { context.F = ComputeByE(context.E); return context; } public static Result2 ToResult(this Logic2Context context) { return new Result2 { B = context.B, E = context.E, F = context.F }; } } // 使用方式 public Result2 Logic2(int a) { return a.ComputeByA() .ComputeByB2() .ComputeByE() .ToResult(); }
方案2:使用元组传递多值(适合简单场景)
如果中间值不多,用元组可以避免定义额外的上下文类,代码更简洁:
public static class LogicTupleExtensions { public static (int B) ComputeByA(this int a) { return (ComputeByA(a)); } public static (int B, int C) ComputeByB(this (int B) tuple) { return (tuple.B, ComputeByB(tuple.B)); } public static (int B, int C, int D) ComputeByC(this (int B, int C) tuple) { return (tuple.B, tuple.C, ComputeByC(tuple.C)); } public static Result1 ToResult(this (int B, int C, int D) tuple) { return new Result1 { B = tuple.B, C = tuple.C, D = tuple.D }; } } // 使用方式 public Result1 Logic1(int a) { return a.ComputeByA() .ComputeByB() .ComputeByC() .ToResult(); }
这种方式的缺点是中间值增多时,元组的参数会变得冗长,可读性下降。
方案3:闭包捕获中间值(不推荐复杂场景)
如果你不想定义额外类或元组,可以利用闭包在链式调用外部捕获中间值,但这种方式会让逻辑分散,不利于维护:
public Result1 Logic1(int a) { int b = 0, c = 0, d = 0; _ = ComputeByA(a) .Tap(val => b = val) // 自定义Tap方法,执行副作用并返回原值 .ComputeByB(val => { c = val; return val; }) .ComputeByC(val => { d = val; return val; }); return new Result1 { B = b, C = c, D = d }; } // 辅助Tap方法 public static T Tap<T>(this T value, Action<T> action) { action(value); return value; }
这种方式的问题是中间值的赋值逻辑和计算逻辑耦合,链式调用的纯粹性被破坏,只适合临时快速实现。
内容的提问来源于stack exchange,提问作者leffe
相关产品推荐
相关产品推荐

