深入 optimizerDuck 的 DI 容器:单例注册与页面自动装配完全指南
【免费下载链接】optimizerDuckFree, open-source Windows optimization tool for performance, privacy, and simplicity.项目地址: https://gitcode.com/GitHub_Trending/op/optimizerDuck
🦆 optimizerDuck 是一款免费的开源 Windows 优化工具,主打性能调优、隐私保护与系统简化。对开发者来说,它最值得学习的是那套干净利落的DI 容器设计:基于Microsoft.Extensions.DependencyInjection的单例注册策略,加上反射扫描 + 自定义特性实现的页面自动装配,让新增一个功能页面几乎零胶水代码。
一、DI 容器搭建:一行 Host 构建器搞定全家桶
optimizerDuck 的依赖注入容器在 App.xaml.cs 中完成搭建,使用的是 .NET 官方的通用主机:
_host = Host.CreateDefaultBuilder() .UseSerilog() .ConfigureServices((context, services) => { /* 全部服务注册 */ }) .Build();在 optimizerDuck.csproj 中可以看到它依赖的两个核心包:Microsoft.Extensions.DependencyInjection和Microsoft.Extensions.Hosting(均为 10.x 版本,目标框架net10.0-windows)。这意味着容器天生自带配置绑定(IOptionsMonitor<AppSettings>)、日志(ILogger<T>)等基础设施,开发者不需要自己写任何工厂代码。
二、单例注册:为什么几乎全部是 AddSingleton
打开注册清单(App.xaml.cs),你会发现一个鲜明的特征——几乎没有 AddTransient,全部是 AddSingleton:
| 类别 | 注册内容 | 位置 |
|---|---|---|
| 🖥️ 窗口层 | MainWindow、MainWindowViewModel | App.xaml.cs#L325-L326 |
| 📄 页面层 | DashboardPage、OptimizePage、SettingsPage等 7 组 Page + ViewModel | App.xaml.cs#L329-L352 |
| ⚙️ 服务层 | OptimizationRegistry、OptimizationService、RevertManager、SystemInfoService等 | App.xaml.cs#L359-L373 |
这种"全单例"策略背后有明确的工程理由:
- WPF 页面是长生命周期对象。页面被导航栏持有,反复创建只会浪费内存并丢失 UI 状态,单例天然匹配。
- 服务持有状态。
OptimizationRegistry要预加载全部优化项、RevertManager要维护回滚记录,这些状态必须唯一共享。 - 服务之间互相引用。全单例避免了"瞬态服务持有单例"导致的 captive dependency(被捕获依赖)陷阱。
一个细节值得关注:WPF-UI 的 UI 服务按接口注册,如services.AddSingleton<ISnackbarService, SnackbarService>()(App.xaml.cs#L320-L322),而业务服务按具体类注册。接口抽象只留在需要替换或隔离的边界(对话框、导航、Toast),内部服务直接引用具体类,更简单直接。
三、页面自动装配:反射扫描 + 特性绑定
真正体现设计功力的,是 optimizerDuck 如何注册那些"优化分类页"。项目里有 7 大优化分类(性能、GPU、电源管理、AI……)和 4 个定制分类,如果手写注册,App.xaml.cs会再膨胀一倍。作者用两个扩展方法解决了它。
3.1 特性声明:把"分类"和"页面"绑在一起
每个优化分类类上都挂着一个自定义特性,例如 Performance.cs:
[OptimizationCategory(typeof(PerformanceOptimizerPage))] public class Performance : IOptimizationCategory { ... }OptimizationCategoryAttribute 只有一个属性PageType,用于声明"这个分类对应哪个 XAML 页面"。定制分类同理,使用 CustomizeCategoryAttribute 携带可选的PageType。
3.2 反射扫描:自动发现所有分类
两个入口方法在App.xaml.cs中被调用:services.AddAllCustomizeCategoryPages()和services.AddAllOptimizationPages()。以 OptimizationPageRegistryExtensions.cs 为例,逻辑只有三步:
- 调用 ReflectionHelper.FindImplementationsInLoadedAssemblies 扫描所有已加载程序集,找出实现了
IOptimizationCategory的类; - 读取每个类上的
OptimizationCategoryAttribute,拿到PageType; - 把页面类型注册为单例,工厂方法稍后解析依赖并创建 ViewModel。
反射扫描本身也做了工程化保护(ReflectionHelper.cs):
- 只扫程序集名以
optimizerDuck开头的程序集,避免遍历系统程序集; - 结果带锁缓存(
_implementationCache),扫描只发生一次; SafeGetTypes捕获ReflectionTypeLoadException,部分类型加载失败时降级返回可用类型,不会让启动流程崩掉。
3.3 工厂方法:延迟解析,组装页面
注册时并不立即创建页面,而是交给一个工厂 lambda(OptimizationPageRegistryExtensions.cs#L29-L32)。当导航栏第一次请求该页面时,工厂才从容器取出 8 个依赖——OptimizationRegistry、OptimizationService、RevertManager、ISnackbarService、IContentDialogService、SystemInfoService、ILogger<T>、IOptionsMonitor<AppSettings>——组装出OptimizationCategoryViewModel,再调用Activator.CreateInstance(pageType, viewModel)生成页面实例(OptimizationPageRegistryExtensions.cs#L36-L65)。
定制分类页的装配逻辑几乎对称,见 CustomizePageRegistryExtensions.cs,区别只是依赖的是CustomizeRegistry并额外注入IRegistryWatcher(用于监听注册表变化实时刷新状态)。页面本身非常薄,如 OptimizationPages.cs 中的PerformanceOptimizerPage,构造函数只接收 ViewModel 并初始化 XAML。
四、这套设计的收益:新增页面只需两件事
理解整个流程后,你会发现扩展成本被压到了最低:
- ✅新增一个优化分类:写一个
IOptimizationCategory实现类,打上[OptimizationCategory(typeof(XxxPage))],再写一个继承OptimizationPage的页面类——不需要改动App.xaml.cs一行代码。 - ✅依赖集中可见:每个页面的依赖清单都明明白白写在工厂方法里,方便排查。
- ✅状态安全:全单例 + WPF 长生命周期页面的特性完全匹配,杜绝重复创建。
- ⚠️代价:反射扫描依赖程序集命名约定(
optimizerDuck*前缀),若拆分子程序集需要注意命名;另外单例泛滥会让单元测试更依赖桩(项目的测试工程 optimizerDuck.Test 就为此提供了StubOptimization等测试替身)。
五、小结:给 WPF 开发者的三个可复用套路
- Host 构建器起步:
Host.CreateDefaultBuilder()一次性获得 DI + 配置 + 日志,UseSerilog()无缝接入文件日志(App.xaml.cs#L292-L301)。 - 单例为王,接口留边界:WPF 桌面应用默认
AddSingleton,仅在需要替换实现的地方引入接口。 - 特性 + 反射做自动装配:用
[XxxCategory(PageType)]风格特性把数据模型与 UI 页面绑定,配合带缓存、带容错的反射扫描,实现"写代码即注册"。
optimizerDuck 把 Windows 系统优化做成了"开箱即点"的体验,而它的 DI 容器设计同样值得你直接搬回自己的 WPF 项目中。✨
【免费下载链接】optimizerDuckFree, open-source Windows optimization tool for performance, privacy, and simplicity.项目地址: https://gitcode.com/GitHub_Trending/op/optimizerDuck
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考