Prism 依赖注入学习笔记:`Container.ResolveMainWindow()` 深入理解
一、一句话概括(秒懂版)
Container.Resolve<MainWindow>()就是告诉 IoC 容器:“请帮我造一个MainWindow对象,如果它需要其他零件,你也帮我一起造好,并自动装进去。”
二、代码上下文(完整版)
public partial class App : PrismApplication
{// 🟢 步骤1:创建主窗口(由框架调用)protected override Window CreateShell(){// ⭐ 核心行:通过容器解析 MainWindow 实例return Container.Resolve<MainWindow>();}// 🟢 步骤2:注册所有服务(提前配置好“零件清单”)protected override void RegisterTypes(IContainerRegistry containerRegistry){// 注册事件聚合器(单例模式)containerRegistry.RegisterSingleton<IEventAggregator, EventAggregator>();// 还可以注册其他服务...// containerRegistry.Register<IDataService, DataService>();}
}
三、逐行拆解(保姆级)
| 代码片段 | 含义 |
|---|---|
Container |
PrismApplication 内置的 IoC 容器实例(默认是 Unity 或 DryIoc)。它在 App 启动时被初始化,并保存在这个属性中。 |
.Resolve<T>() |
容器的“解析”方法。它的职责是:根据类型 T,从容器中返回一个完全构造好的对象。 |
<MainWindow> |
泛型参数,告诉容器“我要的是 MainWindow 类型的对象”。 |
| 整体含义 | 容器会检查 MainWindow 的构造函数,如果有依赖(如 IEventAggregator),它会自动递归创建这些依赖,最后返回组装完成的 MainWindow。 |
四、图解:容器解析过程
调用 Container.Resolve<MainWindow>()│▼
┌─────────────────────────────────────────┐
│ 1. 检查 MainWindow 的构造函数 │
│ public MainWindow(IEventAggregator e)│
└─────────────────────────────────────────┘│▼(发现需要 IEventAggregator)
┌─────────────────────────────────────────┐
│ 2. 去容器注册表查找 IEventAggregator │
│ 注册表:IEventAggregator → EventAggregator │
└─────────────────────────────────────────┘│▼(找到具体实现)
┌─────────────────────────────────────────┐
│ 3. 创建 EventAggregator 实例 │
│ (如果它也有依赖,继续递归) │
└─────────────────────────────────────────┘│▼
┌─────────────────────────────────────────┐
│ 4. 将 EventAggregator 注入到 │
│ MainWindow 构造函数中 │
└─────────────────────────────────────────┘│▼
┌─────────────────────────────────────────┐
│ 5. 返回完整可用的 MainWindow 对象 │
└─────────────────────────────────────────┘
五、为什么不直接 new MainWindow()?
| 写法 | 缺点 | 容器解析的优点 |
|---|---|---|
new MainWindow() |
如果构造函数变了,所有 new 的地方都要改 |
只需修改构造函数,容器自动适配 |
手动 new EventAggregator() 再传入 |
依赖多了以后,代码极其冗长 | 容器自动递归创建所有依赖 |
| 无法在测试时替换为模拟对象 | 单元测试困难 | 只需换一套注册,就能注入 Mock 对象 |
一句话:容器帮你管理对象的“组装过程”,你只需关注“需要什么”,不用关心“怎么造”。
六、Container 是从哪里来的?
你可能好奇:这个 Container 属性我没有定义啊,它从哪冒出来的?
// PrismApplication 内部(简化版)
public abstract class PrismApplication : Application
{// 这个属性由基类提供public IContainerExtension Container { get; private set; }// 启动时会自动调用protected virtual void Initialize(){Container = CreateContainerExtension(); // 创建容器RegisterTypes(Container); // 调用你重写的 RegisterTypesCreateShell(); // 调用你重写的 CreateShell}
}
所以你不需要自己创建容器,Prism 已经在背后帮你准备好了。
七、关键注意事项(踩坑预警)
- ⚠️ 所有构造函数中声明的依赖,都必须在
RegisterTypes中注册过,否则容器会抛出ResolutionFailedException。 - ✅ 注册时注意生命周期:
RegisterSingleton:整个应用共享一个实例(推荐用于IEventAggregator)Register:每次解析都创建新实例
- ✅
MainWindow本身不需要手动注册,因为 Prism 会通过CreateShell自动解析它。
八、总结(便于记忆的口诀)
容器解析三步走:
- 找容器 ——
Container是 Prism 内置的“大仓库”- 下订单 ——
Resolve<MainWindow>()告诉仓库要什么- 自动组装 —— 容器检查构造、递归创建依赖、组装好返回来
九、思考题(自检用)
- 如果
MainWindow的构造函数需要ILogger,但没有注册,会发生什么? RegisterSingleton和Register的区别是什么?- 在单元测试中,如何让容器返回一个模拟的
IEventAggregator?
学习感悟:刚开始接触 DI 时觉得“绕”,但理解了
Resolve本质是“自动递归创建依赖”之后,再看 Prism 代码就豁然开朗了。这种“只依赖接口,不依赖具体实现”的设计,让代码变得非常灵活和可测试。
刚开始接触 DI 时觉得“绕”,但理解了 `Resolve` 本质是“自动递归创建依赖”之后,再看 Prism 代码就豁然开朗了