自定义Either类型适配LINQ查询的SelectMany方法签名应如何定义?
多泛型参数类型的SelectMany签名确定方法
C# 的 LINQ 查询语法采用鸭子类型匹配,不要求类型实现特定接口,只要方法签名符合查询表达式的转换规则即可。你可以直接把 IEnumerable<> 的标准 SelectMany 签名作为模板,按你的自定义类型的结构做替换即可。
泛型参数映射规则
针对 Either<L, R> 这类右偏的双泛型类型(LINQ 操作仅作用于右侧的成功值,左侧为固定的错误通道,出现 Left 直接短路),和标准 IEnumerable 签名的对应关系如下:
| 标准IEnumerable签名的泛型参数 | 对应Either的参数含义 |
|---|---|
| TSource | 源Either包裹的右侧成功值类型 |
| TCollection | collectionSelector返回的Either包裹的右侧成功值类型 |
| TResult | 最终返回的Either包裹的右侧结果值类型 |
| 无对应 | 固定的左侧错误类型L,全程不变 |
正确的Either SelectMany签名
对应替换后的标准签名如下:
public static Either<L, TResult> SelectMany<TSource, TCollection, TResult, L>( this Either<L, TSource> source, Func<TSource, Either<L, TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector ) { // 短路逻辑:任意一步为Left直接返回 if (source.IsLeft) return new Left<L, TResult>(source.Left); var midEither = collectionSelector(source.Right); if (midEither.IsLeft) return new Left<L, TResult>(midEither.Left); // 两个都为Right时,传入两个成功值调用结果转换函数 return new Right<L, TResult>(resultSelector(source.Right, midEither.Right)); }
现有实现的问题
你当前能运行的实现其实是简化的特化版本:
- 你把collectionSelector返回的Either的右侧值类型固定为和源Either一致的R,限制了跨类型的from查询
- 你给resultSelector传入了inner.Left,实际上Left场景已经提前短路,永远不会走到这一步的逻辑,属于无效代码
所有多泛型类型的通用套用方法
不管你的自定义类型有多少个泛型参数,都可以按以下步骤确定SelectMany签名:
- 确定类型的「偏置参数」:即LINQ操作实际作用的那个泛型参数,比如Task的偏置参数是返回值T,Transition的偏置参数是结果值,剩下的泛型参数都是全程固定的通道参数
- 把标准SelectMany签名里所有的
IEnumerable<T>占位符,替换为你的自定义类型,把通道参数固定,把偏置参数位置留出来对应TSource/TCollection/TResult - 保持参数顺序和标准签名完全一致,不需要做额外调整
规范参考
相关规则可以直接查阅C#语言规范的「查询表达式」章节,里面明确规定了多from子句的转换逻辑,该规则对所有类型通用,没有专门针对多泛型参数的特殊文档,因为规则本身就是开放式的。
内容的提问来源于stack exchange,提问作者Tim Barrass
相关产品推荐
相关产品推荐

