c#设计模式应解决具体问题而非炫技:单例优先用lazy或di容器;工厂方法与抽象工厂适用场景不同;观察者推荐事件委托;策略模式需职责分离,避免过度设计。

直接说结论:C#里用设计模式不是为了“炫技”,而是为了解决具体问题——比如对象创建失控、行为耦合太紧、状态分支爆炸。盲目套23种模式反而会让代码更难维护。
单例模式别手写双重检查锁
很多人一上来就照搬 if (_instance == null) { lock (...) { if (_instance == null) ... } },但.NET 4.0+ 有更安全简洁的替代方案。
-
Lazy<t></t>是首选:它内置线程安全、延迟初始化、且无锁开销,private static readonly Lazy<singleton> _instance = new Lazy<singleton>(() => new Singleton());</singleton></singleton> - 别忽略序列化风险:反序列化会绕过私有构造函数,导致新实例;加
[Serializable]的同时必须实现ISerializable或标记构造函数为protected并重写GetObjectData - ASP.NET Core 中更推荐用
IServiceCollection.AddSingleton<t>()</t>,由容器统一管理生命周期,避免手动单例与 DI 容器冲突
工厂方法和抽象工厂别混用
工厂方法(FactoryMethod)解决的是“一类产品中选一个具体实现”,抽象工厂(AbstractFactory)解决的是“一组配套产品整体切换”。混淆会导致接口膨胀、子类爆炸。
- 用
FactoryMethod的典型场景:日志组件支持FileLogger/DbLogger,但每次只用一种,且未来可能加CloudLogger—— 只需新增子类,不改现有工厂基类 - 用
AbstractFactory的典型场景:UI 控件需要同时提供WinButton+WinCheckbox或MacButton+MacCheckbox,不能混搭 —— 此时工厂返回的是“族”,不是单个对象 - C# 泛型可简化抽象工厂:用
IGUIFactory<tbutton tcheckbox></tbutton>替代一堆接口,但要注意类型约束(where TButton : IButton, new())是否合理
观察者模式优先用事件和委托
硬套 GoF 的 IObserver/IObservable 接口在 C# 里往往过度设计。原生事件机制更轻量、更符合语言习惯。
- 直接用
public event EventHandler<eventargs> StateChanged;</eventargs>,比手写Attach/Notify方法少一半代码,且天然支持多播和空安全调用(?.Invoke) - 自定义事件参数时继承
EventArgs,不要裸传object;.NET 6+ 可用泛型EventHandler<teventargs></teventargs>提升类型安全 - 注意内存泄漏:用实例方法订阅事件时,发布者会长期持有订阅者引用;解订阅不及时或用 Lambda 订阅(无法解订)都可能导致对象无法释放
策略模式别让上下文承担太多职责
Strategy 模式的核心是“算法可替换”,但常见错误是把策略选择逻辑、缓存、日志、异常兜底全塞进 Context 类里。
- 策略本身应无状态、无副作用;状态应由上下文或外部传入,避免策略内部维护
_cache或调用ILogger - 策略选择建议外置:用工厂或配置驱动(如
appsettings.json+IOptionsMonitor),而不是在Context构造函数里写switch (type) - 性能敏感场景慎用虚方法调用:如果策略执行极频繁(如每毫秒调用数百次),考虑用
Func<t></t>或delegate直接注入,避开虚表查找
真正难的不是写出符合 UML 图的模式,而是判断某个问题值不值得引入模式——比如两个 if 分支就用策略,三处 new 同一个类就搞工厂,这种节奏容易把简单问题复杂化。模式是止痛药,不是维生素。










