对象适配器是c#中唯一现实可行的adapter模式实现路径,类适配器因不支持多重继承及sealed类等限制基本不可用;其必须遵守构造函数接收实例、idisposable透传、未支持方法抛notsupportedexception三条铁律。

在 C# 里,Adapter 模式不是“怎么写好看”,而是“怎么写不翻车”——对象适配器是唯一现实可行的路径,类适配器在绝大多数项目里连编译都过不去。
为什么类适配器在 C# 中基本不可用
它要求适配器同时继承 Adaptee 并实现 ITarget,但 C# 不支持多重继承,且实际中几乎总会撞上以下任一硬限制:
-
Adaptee是sealed类(比如NpgsqlConnection、ConfigurationSection、HttpClientHandler),直接编译失败 -
Adaptee构造函数带参数,而你根本不需要初始化它的全部状态,却被迫写: base(...) - 你无法对
Adaptee做Mock或替换,单元测试只能绕开或写一堆桩 - 后续
Adaptee加了新虚方法,你的子类不会自动继承行为变更,维护成本陡增
对象适配器必须遵守的三条铁律
它靠组合 + 接口实现,看似简单,但漏掉任意一条都会在运行时出问题:
- 构造函数必须显式接收
Adaptee实例,禁止在内部new Adaptee()—— 否则生命周期失控,DI 容器注册失效 - 若
Adaptee实现了IDisposable,你的Adapter也必须实现,并在Dispose()中调用_adaptee.Dispose() - 对目标接口中未支持的方法(如
ILogger.BeginScope<tstate></tstate>),不能留空体或返回null,必须显式throw new NotSupportedException()
什么时候真该写 Adapter 类,而不是用更轻的替代方案
别一看到“接口不匹配”就新建 Adapter 类。多数时候,这些方式更安全、更少出错:
- 字段名/结构一致?用
record直接构造:var dto = new UserDto(user.Name, user.Email); - 只差一两个方法?加
static class扩展方法,零额外类型、零堆栈污染 - 需要隐式转换?定义
implicit operator,编译器自动触发,无运行时开销 - 只是 JSON ↔ XML 或字符串格式转换?这类逻辑应前置到 DTO 层或缓存,别塞进
Adapter方法体里
对象适配器最容易被忽略的性能与调试陷阱
它引入一层引用转发,表面无害,实则藏坑:
- 每次调用都多一次虚方法分发或委托调用,高频路径下可观测到微秒级延迟累积
- 异常堆栈里显示的是
Adapter.Request(),但实际崩溃点在_adaptee.SpecificRequest(),不设断点容易误判 - 若
Adaptee是Singleton而Adapter注册为Scoped,组合关系能解耦生命周期;但反过来(Adaptee是Scoped,Adapter是Singleton)会引发ObjectDisposedException
真正要动手写 Adapter 类,往往只发生在两种不可绕开的场景:桥接第三方 sealed 类(如 LegacyPaymentService),或对接旧同步 API 与新异步接口(如把 StreamReader.ReadToEnd() 包装成 IAsyncEnumerable<string></string>)。其他情况,先问自己一句:这个转换,能不能用一行 record 或一个扩展方法解决?











