策略模式在c#中真正卡点是接口契约模糊、di注册错位、运行时解析失败三处;应定义泛型接口ichargeprocessor,用addkeyedscoped注册,getrequiredkeyedservice解析,context仅分发不决策。

策略模式在 C# 里真正卡住人的,从来不是“怎么写几个类”,而是接口契约模糊、DI 注册错位、运行时解析失败这三处。只要这三点踩准了,后续扩展几乎零成本。
IStrategy 接口必须带泛型输入输出边界
空接口 IStrategy 或宽泛命名的 IPaymentStrategy 是典型陷阱:它不声明输入、不约定输出、不说明同步/异步,新加一个策略时全靠注释猜行为。结果就是调用方传 string,另一个传 IDictionary<string object></string>,最后只能靠 object 强转或泛型堆砌。
正确做法是按真实调用边界定义泛型接口:
-
IChargeProcessor<trequest tresponse></trequest>,其中TRequest是明确 DTO(如AlipayChargeRequest) -
TResponse是统一返回结构(如Result<chargeresult></chargeresult>) - 方法签名必须体现执行语义:
Task<tresponse> ProcessAsync(TRequest req)</tresponse>,而非笼统的Execute() - 禁止在接口里加
Init()、SetLogger(ILogger)、IsEnabled这类状态或配置字段——这些该由构造函数注入或外部控制
.NET 8+ 必须用 AddKeyedScoped 而非手写 Dictionary
手动维护 Dictionary<string istrategy></string> 并用 new AlipayStrategy() 填充,短期看着快,一接入多租户、插件热加载或 Scoped 服务就崩:Key 拼错无提示、重复注册不报错、作用域泄漏、单元测试无法替换。
交给 DI 容器管才是正解:
- 注册全部策略实现为
AddTransient<istrategy alipaystrategy>()</istrategy>+AddTransient<istrategy wechatstrategy>()</istrategy> - 需要按名称路由?用元数据标记比字符串 Key 更安全:
[StrategyName("alipay")],解析时用GetServices<istrategy>()</istrategy>+ 反射读取特性 - .NET 8+ 推荐
AddKeyedScoped<istrategy alipaystrategy>("alipay")</istrategy>,调用时直接provider.GetRequiredKeyedService<istrategy>("alipay")</istrategy>
“No service for type 'X' has been registered” 错误怎么快速定位
这个错误不是策略类写错了,而是 DI 链断在某一层。常见原因有三:
- 策略类本身没注册(漏掉
AddTransient<istrategy xxxstrategy>()</istrategy>) - 策略构造函数依赖了未注册的服务,比如
IHttpClientFactory或IOptions<myconfig></myconfig>——注意IOptions<t></t>必须显式调用AddOptions<myconfig>()</myconfig> - 生命周期冲突:策略是
Transient,却注入了一个Singleton服务,而该服务又依赖Scoped服务
排查路径:从报错策略类开始,逐个检查构造函数参数,确认每个类型都在 Program.cs 里显式注册过;用 ServiceProvider.GetService<ienumerable>>()</ienumerable> 打印所有已注册策略,看有没有漏掉的实现类。
Context 类不能偷偷做策略路由
很多人写 OrderProcessor 时,在 Execute() 里根据 order.PaymentMethod 去 switch 或查字典选策略,这等于把策略选择逻辑又塞回了上下文,跟写 if-else 没本质区别。
Context 构造函数只接收 IStrategy,不接收 string name、Type、IConfiguration 节点;禁用 new SqlPaymentStrategy() 这类硬编码实例化——它绕过 DI,导致生命周期失控(比如 Scoped 策略被当 Singleton 用);需要运行时切换?提供 SetStrategy(IStrategy s) 方法,由上层(比如 API Controller)决定传哪个。
策略最难的从来不是写几个类,而是划清边界:哪些该进策略、哪些该由上下文传入、哪些该交由装饰器或中间件处理——一旦混淆,泛型套娃和 object 强转就接踵而至。










