协变用于消息消费端(out),使具体处理器可赋值给抽象处理器;逆变用于分发策略(in),使基类策略能处理子类消息;二者分层组合实现类型安全、解耦的消息监听架构。

直接用协变和逆变构建消息监听端,核心不是“加功能”,而是让类型关系贴合消息流向:生产者发具体消息,消费者按抽象契约处理;而反向配置时,又能让通用处理器安全接入更窄类型的通道。关键在接口设计阶段就锁定数据方向。
协变用于消息消费端——向上兼容具体消息类型
定义只读消息处理器接口,用 out 标记泛型参数:
IHandler
-
TMessage GetMessage()—— 合法,协变允许IHandler赋值给IHandler -
IEnumerable—— 合法,GetBatch() IEnumerable可转为IEnumerable - 不出现
void Handle(TMessage msg)—— 这是输入行为,协变禁止
实际部署时,一个统一的监听服务可注册 IHandler,而不同业务模块提供 IHandler 或 IHandler,自动适配,无需类型转换或反射。
逆变用于消息分发策略——向下兼容处理能力范围
定义策略判定接口,用 in 标记泛型参数:
IHandlerSelector
-
bool CanHandle(TMessage msg)—— 合法,逆变允许IHandlerSelector赋值给IHandlerSelector -
string GetRoutingKey(TMessage msg)—— 合法,基类策略能判断所有子类消息 - 不出现
TMessage CreateDefault()—— 这是输出行为,逆变禁止
这样,一个通用路由组件可持有 IHandlerSelector,而具体业务只需实现 IHandlerSelector,系统自动将其升格使用,策略逻辑不感知具体子类型。
组合协变+逆变构建双层抽象监听器
不依赖单一泛型,而是分层解耦:
- 底层接收原始字节流 → 反序列化为
object或基类IMessage - 中间层用逆变接口
IValidator做通用校验(如签名、时效) - 上层用协变接口
IProcessor执行领域逻辑,支持IProcessor注册进同一容器 - 最终由调度器协调:先过
IValidator(逆变),再派发给匹配的IProcessor(协变)
这种结构天然隔离变化点:新增消息类型只需添加子类和对应 processor,不改 validator 或调度器;升级校验规则也只动逆变侧,不影响业务处理逻辑。
避坑要点:边界必须清晰,不可混用
协变和逆变不是语法糖,是类型安全契约。实战中常见错误:
- 在
IHandler里写void Process(T msg)→ 编译失败,立刻暴露设计缺陷 - 把
List当协变用 → 不支持,必须用IEnumerable或IReadOnlyList - 对类(class)而非接口/委托使用 in/out → 无效,C# 不支持泛型类的变型
- 跨域传递协变接口实例时未序列化具体类型信息 → 运行时报错,需配合元数据或约定类型名
真正高内聚低耦合的消息监听端,不是靠继承树堆出来,而是靠 in/out 把“谁读”和“谁写”的责任在编译期就切开。










