策略模式在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 6+ 别手写 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服务(如DbContext)
排查路径很直接:从报错策略类开始,逐个检查其构造函数参数类型,确认每个都在 Program.cs 中被 services.AddXXX 显式注册过。临时加一行 Console.WriteLine(string.Join(", ", provider.GetServices<istrategy>().Select(s => s.GetType().Name)))</istrategy>,能立刻看到哪些策略实际被加载了。
Context 类唯一职责是分发,不是决策也不是工厂
很多人把 Context 写成条件判断中枢,比如在 Execute() 里写 if (type == "alipay") return _provider.GetService<alipaystrategy>().Process(...)</alipaystrategy> ——这等于把策略选择逻辑硬编码进上下文,彻底失去可测试性和可替换性。
Context 应极度轻量:
- 构造函数只接收
IStrategy或IServiceProvider(用于延迟解析),绝不接收string strategyName或Type - 禁用
new AlipayStrategy(),所有实例必须来自 DI - 如果需运行时切换,提供
SetStrategy(IStrategy s)方法,而非在执行中 if-else - 若策略需传参(如超时、租户 ID),用工厂委托注册:
services.AddTransient<istrategy>(sp => new AlipayStrategy(sp.GetRequiredService<ihttpclientfactory>(), timeout: 10000))</ihttpclientfactory></istrategy>
最易被忽略的一点:策略本身不该持有上下文状态。比如把 HttpContext、IServiceProvider 塞进策略字段——这会让策略变成有状态、不可复用、难以单元测试的怪物。所有外部依赖,必须通过构造函数注入,且仅限真正需要的服务。










