
简介本资源是面向C#中高级开发者的设计模式实战学习包聚焦软件架构设计能力提升帮助程序员深入理解并落地应用经典设计模式解决代码可维护性差、扩展困难、耦合度高等实际工程问题。压缩包为RAR格式大小3.26MB虽未提供具体文件总数与类型明细但结合描述可知其包含10种核心模式单例、工厂、抽象工厂、建造者、适配器、模板方法、观察者、装饰、代理、组合的完整C#源码实现每种模式均附带可运行示例与关键注释便于对照原理逐行分析。资源已获301人学习下载适合正在参与企业级开发、准备技术面试或系统重构的工程师通过即学即用的代码范例快速掌握模式适用边界、C#特有实现技巧如INotifyPropertyChanged数据绑定、静态密封单例、反射动态代理等并建立面向对象设计的结构化思维。1. 这不是设计模式“教科书”而是一份能直接编译、调试、打断点的 C# 实战源码包你有没有试过读完《Head First 设计模式》第 7 章合上书想写个 Observer Strategy 组合的事件分发器结果卡在IObserverT和IAsyncEnumerableT的生命周期对齐上或者照着 UML 类图手撸一个 State 模式运行时状态切换突然失效调试半天发现是this引用被闭包捕获导致状态机对象被意外复用这不是你理解力的问题——是缺一份带完整上下文、可运行、有真实业务切口、且每个类都打了断点注释的 C# 设计模式源码。这份「C#设计模式源码」不是教学幻灯片也不是抽象接口堆砌它是一套经 VS 2022 .NET 6/7/8 多版本验证的、覆盖 23 种 GoF 模式核心变体的可执行工程含 Console、WPF、ASP.NET Core WebAPI 三类宿主每个模式都绑定一个微缩但真实的业务场景比如 Command 模式配订单撤销重做队列Mediator 模式驱动多窗口协同的库存预警弹窗Decorator 模式实现动态日志脱敏策略链。它专为两类人准备刚学完理论想立刻验证逻辑的新手以及正在重构遗留系统、需要快速比对模式落地边界的中高级开发者。别再对着静态代码图猜调用栈了——这份资源让你把设计模式真正“跑起来”。2. 从零加载项目结构解剖与运行验证路径2.1 项目组织逻辑为什么按“模式分类宿主类型”双维度划分整个源码包采用三层物理结构根目录Patterns/存放所有模式实现的核心类库.csproj类型为ClassLibrary不依赖任何 UI 或 Web 框架纯 .NET Standard 2.1 兼容。宿主目录Hosts/包含三个独立可执行项目ConsoleHost/控制台程序用于验证单线程、无状态场景下的模式行为如 Singleton 线程安全初始化、Flyweight 内存复用WpfHost/WPF 应用重点演示 UI 层模式如 MVVM 中的 Composite Command 组合、Observer 驱动的实时数据绑定WebApiHost/ASP.NET Core WebAPI展示服务端模式如 Factory 创建不同数据库仓储、Proxy 控制第三方 API 调用熔断。测试目录Tests/xUnit 测试项目每个测试用例命名直指模式关键契约如When_StatePattern_Transitions_Should_Not_Recreate_State_Objects。提示这种结构不是为了炫技而是解决实际协作痛点。某公司重构支付模块时后端团队只关心Patterns/和WebApiHost/前端团队则聚焦WpfHost/的 MVVM 实现——物理隔离避免误改逻辑耦合通过接口定义保证。2.2 快速启动三步完成首个模式验证以 Factory Method 为例我们以最易混淆的 Factory Method 模式切入验证其如何解耦产品创建与使用# 步骤 1打开解决方案确保已安装 .NET 6 SDK 及以上 dotnet sln open CSharpDesignPatterns.sln # 步骤 2定位到 FactoryMethod 示例在 Patterns/Creational/FactoryMethod/ 目录下 # 步骤 3运行 WpfHost点击主界面 Factory Demo 按钮此时会弹出一个模拟电商后台的窗口左侧选择“支付方式”Alipay / WechatPay / CreditCard右侧实时显示该支付方式生成的订单对象含OrderId,Amount,GatewayUrl。关键在于——所有支付对象均由IPaymentFactory.CreatePayment()创建而具体工厂AlipayFactory等仅在 WpfHost 的 ViewModel 中注入Patterns/ 类库中完全不引用任何工厂实现类。// Patterns/Creational/FactoryMethod/IPaymentFactory.cs public interface IPaymentFactory { IPayment CreatePayment(decimal amount); } // Patterns/Creational/FactoryMethod/IPayment.cs public interface IPayment { string OrderId { get; } decimal Amount { get; } string GatewayUrl { get; } } // WpfHost/ViewModels/PaymentDemoViewModel.cs宿主层 private readonly IPaymentFactory _factory; public PaymentDemoViewModel(IPaymentFactory factory) // 依赖注入具体工厂 { _factory factory; } public void OnPaymentTypeSelected(string type) { var payment _factory.CreatePayment(99.9m); // 调用抽象方法不关心谁创建 Display(payment); }逻辑说明Patterns/类库只定义IPaymentFactory和IPayment不包含任何new AlipayPayment()WpfHost/通过 DI 容器注册具体工厂如services.AddSingletonIPaymentFactory, AlipayFactory()实现创建逻辑与使用逻辑的彻底分离。这是 Factory Method 的本质让子类决定实例化哪个类从而将对象创建延迟到子类。参数说明CreatePayment(decimal amount)的amount参数并非随意设计——它强制要求工厂在创建时即注入业务上下文金额避免后续通过属性赋值导致对象处于不一致状态常见坑var p new AlipayPayment(); p.Amount 99.9m;—— 此时GatewayUrl可能因未初始化而为空。2.3 核心类库依赖与兼容性验证Patterns/类库的.csproj文件明确声明了最小依赖!-- Patterns/Patterns.csproj -- Project SdkMicrosoft.NET.Sdk PropertyGroup TargetFrameworknetstandard2.1/TargetFramework Nullableenable/Nullable /PropertyGroup ItemGroup PackageReference IncludeMicrosoft.Extensions.DependencyInjection Version7.0.0 / /ItemGroup /Project这意味着✅ 可直接被 .NET Framework 4.7.2、.NET Core 3.1、.NET 5/6/7/8 项目引用✅ 不依赖 WinForms/WPF/ASP.NET 特定组件纯逻辑复用❌ 不支持 .NET Standard 2.0因使用了SpanT等特性需 2.1。验证方法新建一个空 .NET 5 控制台项目执行dotnet add reference ../Patterns/Patterns.csproj然后在Program.cs中编写using Patterns.Creational.Singleton; var instance1 ThreadSafeSingleton.GetInstance(); var instance2 ThreadSafeSingleton.GetInstance(); Console.WriteLine($Same instance? {ReferenceEquals(instance1, instance2)}); // 输出 True若编译通过且输出True证明基础兼容性已就绪。3. 深度解析五个高频模式的 C# 特性化实现细节3.1 Singleton线程安全 ≠ 性能最优.NET 6 的LazyT与static构造函数取舍传统双重检查锁Double-Checked Locking在 C# 中已非首选。本源码包提供三种实现并标注适用场景实现方式代码位置线程安全初始化时机推荐场景static构造函数Patterns/Creational/Singleton/StaticConstructorSingleton.cs✅CLR 保证类首次访问时简单无参初始化如配置管理器LazyTPatterns/Creational/Singleton/LazySingleton.cs✅LazyThreadSafetyMode.ExecutionAndPublication首次Value访问时需延迟初始化且构造耗时如连接池双重检查锁Patterns/Creational/Singleton/DoubleCheckedLockingSingleton.cs✅正确实现首次调用GetInstance()时需兼容旧版 .NET Framework 且必须显式控制锁粒度关键差异点LazyT在 .NET 6 中性能接近static构造函数但static构造函数无法传参。例如若 Singleton 需读取appsettings.json中的数据库连接字符串则必须用LazyT包装工厂方法public class DatabaseConnectionSingleton { private static readonly LazyDatabaseConnectionSingleton _instance new LazyDatabaseConnectionSingleton(() new DatabaseConnectionSingleton( Configuration.GetConnectionString(Default))); // 构造函数接收参数 private readonly string _connectionString; private DatabaseConnectionSingleton(string connectionString) _connectionString connectionString; public static DatabaseConnectionSingleton Instance _instance.Value; }注意LazyT的Value属性是线程安全的但Instance属性本身不是线程安全的——不过由于Value已保证单例此处无风险。3.2 Observer从IObservableT到ChannelT响应式编程的演进陷阱很多教程仍停留在IObservableTIObserverT的手动订阅但本源码包在WebApiHost/中展示了更现代的ChannelT实现// Patterns/Behavioral/Observer/ChannelBasedStockObserver.cs public class StockPricePublisher { private readonly ChannelStockUpdate _channel Channel.CreateUnboundedStockUpdate(); public IAsyncEnumerableStockUpdate Subscribe() _channel.Reader.ReadAllAsync(); public async Task PublishAsync(StockUpdate update) { await _channel.Writer.WriteAsync(update); // 线程安全写入 } } // WebApiHost/Controllers/StockController.cs [HttpGet(stream)] public ActionResult StreamStockUpdates() { var publisher _serviceProvider.GetRequiredServiceStockPricePublisher(); return new StreamingResponse(publisher.Subscribe()); // 返回 IAsyncEnumerable }现象对比旧式IObservableT需手动管理Subscribe()返回的IDisposable易内存泄漏ChannelT天然支持异步流、背压Backpressure、取消令牌且Reader.ReadAllAsync()返回的IAsyncEnumerable可直接被 ASP.NET Core 的StreamResult消费。但陷阱在于Channel.CreateUnbounded不限制缓冲区大小若生产者远快于消费者内存会无限增长。生产环境应改用Channel.CreateBoundedStockUpdate(new BoundedChannelOptions(100))并处理WriteAsync的false返回值表示写入失败。3.3 Decorator用IAsyncEnumerableT实现日志链而非传统继承传统 Decorator 常用继承class LoggingDecorator : IService但 C# 的async方法无法被虚方法完美覆盖。本源码包采用组合 IAsyncEnumerableT实现动态装饰// Patterns/Structural/Decorator/LoggingDecorator.cs public class LoggingDecoratorT : IAsyncEnumerableT { private readonly IAsyncEnumerableT _inner; private readonly ILogger _logger; public LoggingDecorator(IAsyncEnumerableT inner, ILogger logger) { _inner inner; _logger logger; } public IAsyncEnumeratorT GetAsyncEnumerator(CancellationToken cancellationToken default) new LoggingAsyncEnumeratorT(_inner.GetAsyncEnumerator(cancellationToken), _logger); } // 内部枚举器自动记录每次 MoveNextAsync 的耗时与结果 internal class LoggingAsyncEnumeratorT : IAsyncEnumeratorT { private readonly IAsyncEnumeratorT _inner; private readonly ILogger _logger; private readonly Stopwatch _stopwatch Stopwatch.StartNew(); public LoggingAsyncEnumerator(IAsyncEnumeratorT inner, ILogger logger) { _inner inner; _logger logger; } public ValueTaskbool MoveNextAsync() LogExecution(() _inner.MoveNextAsync()); private async ValueTaskbool LogExecution(FuncValueTaskbool operation) { _stopwatch.Restart(); var result await operation(); _stopwatch.Stop(); _logger.LogInformation(MoveNextAsync completed in {ElapsedMs}ms, result: {Result}, _stopwatch.ElapsedMilliseconds, result); return result; } public T Current _inner.Current; public ValueTask DisposeAsync() _inner.DisposeAsync(); }此设计允许在任意IAsyncEnumerableT上叠加日志、熔断、重试等装饰器且无需修改原始实现——这才是 Decorator 的灵魂动态地给对象添加职责而不修改其结构。3.4 MediatorWPF 中的强类型消息总线规避Messenger.DefaultWpfHost/使用自研StrongTypedMediator替代 MVVM Light 的Messenger.Default原因在于后者基于string消息名编译期无法检查// Patterns/Behavioral/Mediator/StrongTypedMediator.cs public interface IMediator { Task PublishTMessage(TMessage message, CancellationToken ct default) where TMessage : class; void RegisterTMessage(FuncTMessage, Task handler) where TMessage : class; } // WpfHost/ViewModels/InventoryViewModel.cs public class InventoryViewModel : BaseViewModel { private readonly IMediator _mediator; public InventoryViewModel(IMediator mediator) { _mediator mediator; _mediator.RegisterLowStockAlert(OnLowStockAlert); // 编译期检查LowStockAlert 类型存在 } private async Task OnLowStockAlert(LowStockAlert alert) { // 弹窗逻辑 await MessageBox.Show($库存告急{alert.ProductName}); } }优势✅ 注册时LowStockAlert类型错误会在编译时报出而非运行时Messenger.Send(new LowStockAlert())后找不到订阅者✅PublishTMessage支持泛型约束如where TMessage : IIntegrationEvent强制消息契约✅ 所有消息处理器在IMediator实例内统一管理生命周期避免Messenger.Default的静态全局状态污染。3.5 Strategy用DictionaryType, FuncT替代switch支持运行时热插拔传统 Strategy 常用switch (type)分发但难以扩展。本源码包在WebApiHost/中实现基于字典的策略注册// Patterns/Behavioral/Strategy/DynamicStrategyResolver.cs public class DynamicStrategyResolverTContext, TResult { private readonly DictionaryType, FuncTContext, TResult _strategies new(); public void RegisterTStrategy(FuncTContext, TResult strategy) where TStrategy : class { _strategies[typeof(TStrategy)] strategy; } public TResult ExecuteTStrategy(TContext context) where TStrategy : class { if (!_strategies.TryGetValue(typeof(TStrategy), out var strategy)) throw new InvalidOperationException($No strategy registered for {typeof(TStrategy).Name}); return strategy(context); } } // WebApiHost/Startup.cs 注册 var resolver new DynamicStrategyResolverOrderContext, string(); resolver.RegisterAlipayStrategy((ctx) $alipay://pay?order{ctx.OrderId}); resolver.RegisterWechatStrategy((ctx) $weixin://pay?order{ctx.OrderId}); // 可在运行时通过 API 动态注册新策略如 PayPalStrategy此设计使策略真正“可插拔”新增支付方式只需调用RegisterPayPalStrategy无需修改任何switch或if-else分支。4. 避坑指南五个血泪经验总结的典型翻车现场4.1 现象State 模式中状态切换后this引用指向旧状态对象导致行为错乱原因在状态类内部直接调用context.SetState(new ConcreteStateB())时若ConcreteStateB构造函数中又调用了context.DoSomething()而DoSomething()内部又调用了context.State.Handle()此时context.State可能仍是旧状态对象因SetState尚未完成赋值。C# 的字段赋值非原子操作且状态类常持有对context的引用形成循环调用死锁。解决强制状态切换为“两阶段”先由 Context 保存新状态类型再在下一帧或Task.Yield()后执行状态变更。源码中Patterns/Behavioral/State/TwoPhaseStateContext.cs提供标准实现SetState方法内部调用ScheduleStateChange(typeof(NewState))并在ExecuteNextAction()中完成赋值。4.2 现象Observer 模式中多个订阅者共用一个CancellationTokenSource导致一个取消全部失效原因为简化代码开发者常在 Publisher 中创建单例CancellationTokenSource所有Subscribe()返回的IAsyncEnumerable共享其Token。当某个客户端断开连接如浏览器关闭Publisher 主动Cancel()所有其他活跃订阅者也被强制终止。解决为每个订阅者生成独立CancellationTokenSource。Patterns/Behavioral/Observer/PerSubscriberCtsPublisher.cs中Subscribe()方法内部创建新Cts并将Cts.Token传递给Channel.Reader.ReadAllAsync(cts.Token)。同时在DisposeAsync()中释放Cts避免内存泄漏。4.3 现象Decorator 模式嵌套过深如new LoggingDecorator(new RetryDecorator(new HttpDecorator()))导致栈溢出原因每个 Decorator 都继承自同一基类或实现同一接口调用链过长时JIT 编译器可能无法内联递归调用深度超限。尤其在async方法中await会拆分调用栈加剧问题。解决改用“装饰器链表”而非嵌套对象。Patterns/Structural/Decorator/ChainOfResponsibilityDecorator.cs定义IDecoratorChainT接口所有装饰器实现HandleAsync(T input)并调用Next?.HandleAsync(input)。初始化时构建单链表var chain new LoggingDecorator().Then(new RetryDecorator()).Then(new HttpDecorator())避免对象嵌套层级。4.4 现象Factory Method 模式中子类工厂返回null父类方法未做空值检查引发NullReferenceException原因GoF 原著强调“子类决定实例化哪个类”但未规定子类必须返回非空对象。实际业务中工厂可能因配置缺失、权限不足返回null而 Creator 类如Creator.CreateProduct()直接调用product.Use()崩溃。解决在抽象 Creator 中强制空值防护。Patterns/Creational/FactoryMethod/NullSafeCreator.cs的CreateProduct()方法末尾添加if (product null) throw new InvalidOperationException($Factory {GetType().Name} returned null for product); return product;同时所有具体工厂文档明确要求“必须返回有效实例禁止返回 null”。4.5 现象Command 模式中Undo 操作执行后Redo 队列未清空导致重复执行原因典型实现是维护StackICommand _undoStack和StackICommand _redoStack。当用户执行 Undo 后_undoStack.Pop()的命令被压入_redoStack但若用户紧接着执行新 Command非 Redo_redoStack未被清空后续 Redo 会执行过期命令。解决在Execute(ICommand command)方法中执行新命令前清空_redoStack。Patterns/Behavioral/Command/RedoSafeCommandManager.cs的Execute方法包含public void Execute(ICommand command) { _redoStack.Clear(); // 关键新命令使 Redo 失效 command.Execute(); _undoStack.Push(command); }此规则符合用户直觉执行新操作即放弃重做历史。5. 进阶技巧用 Roslyn 分析器自动检测模式误用5.1 为什么需要静态分析——模式是“约定”不是“语法”设计模式的本质是解决特定问题的约定俗成的结构编译器无法识别。例如一个类名为UserManager并实现了IUserRepository它可能是 Strategy也可能是 Facade甚至只是普通服务——仅靠命名和接口无法判定。但某些误用会留下可检测的“指纹”Singleton 类被new调用违反单例约束Observer 的Subscribe()方法未返回IDisposable或未处理取消违背响应式契约Decorator 类未实现被装饰接口失去透明性Factory Method 的 Creator 类未声明abstract或子类未override创建方法破坏模式骨架。这些都不是编译错误却是典型的“模式形似神散”翻车点。5.2 实战编写一个检测 Singleton 误用的 Roslyn 分析器我们以最易犯错的 Singleton 为例创建一个分析器扫描所有标记[Singleton]特性的类并检查是否被new调用// Analyzer/SingletonUsageAnalyzer.cs [DiagnosticAnalyzer(LanguageNames.CSharp)] public class SingletonUsageAnalyzer : DiagnosticAnalyzer { public const string DiagnosticId CSP001; private static readonly LocalizableString Title Singleton instantiation detected; private static readonly LocalizableString MessageFormat Avoid new {0}() for singleton classes. Use instance property instead.; private static readonly DiagnosticDescriptor Rule new( DiagnosticId, Title, MessageFormat, DesignPatterns, DiagnosticSeverity.Warning, isEnabledByDefault: true); public override ImmutableArrayDiagnosticDescriptor SupportedDiagnostics ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); // 监听对象创建表达式new XXX() context.RegisterSyntaxNodeAction(AnalyzeObjectCreation, SyntaxKind.ObjectCreationExpression); } private void AnalyzeObjectCreation(SyntaxNodeAnalysisContext context) { var creation (ObjectCreationExpressionSyntax)context.Node; var typeName creation.Type.ToString(); // 获取语义模型判断该类型是否标记 [Singleton] var model context.SemanticModel; var symbol model.GetSymbolInfo(creation.Type).Symbol as INamedTypeSymbol; if (symbol null || !symbol.GetAttributes().Any(a a.AttributeClass?.Name Singleton)) return; // 报告警告 var diagnostic Diagnostic.Create(Rule, creation.GetLocation(), typeName); context.ReportDiagnostic(diagnostic); } }配套的代码修复提供器CodeFixProvider可自动将new UserManager()替换为UserManager.Instance// Analyzer/SingletonCodeFixProvider.cs [ExportCodeFixProvider(LanguageNames.CSharp, Name nameof(SingletonCodeFixProvider))] [Shared] public class SingletonCodeFixProvider : CodeFixProvider { public sealed override FixAllProvider GetFixAllProvider() WellKnownFixAllProviders.BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var diagnostic context.Diagnostics.First(); var creation root.FindNode(diagnostic.Location.SourceSpan) as ObjectCreationExpressionSyntax; if (creation null) return; // 构建替换节点UserManager.Instance var instanceAccess SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, creation.Type, SyntaxFactory.IdentifierName(Instance)); var newRoot root.ReplaceNode(creation, instanceAccess); var newDocument context.Document.WithSyntaxRoot(newRoot); context.RegisterCodeFix( CodeAction.Create(Replace new with singleton instance, ct Task.FromResult(newDocument)), diagnostic); } }5.3 集成与验证三步启用分析器编译分析器项目将上述代码放入Analyzer/目录编译为SingletonAnalyzer.dll在目标项目中引用在CSharpDesignPatterns.sln的Directory.Build.props中添加ItemGroup Analyzer Include$(MSBuildThisFileDirectory)Analyzer\bin\Debug\SingletonAnalyzer.dll / /ItemGroup触发检测在WpfHost/中故意写var mgr new UserManager();VS 即刻标黄并提示警告光标悬停显示“Replace new with singleton instance”按Ctrl.即可一键修复。从那以后我每次引入新设计模式时都强制走一遍 Roslyn 分析器校验先写分析规则再写模式实现最后用分析器扫一遍所有调用点。这招看似多花 20 分钟却省去后期排查“为什么 Singleton 不单例”的 3 小时——因为编译器已经替我把住了第一道门。希望帮到你。本文还有配套的精品资源点击获取