Prism 依赖注入学习笔记:`Container.ResolveMainWindow()` 深入理解

📅 2026/7/10 8:56:27 👁️ 阅读次数 📝 编程学习
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 自动解析它。

八、总结(便于记忆的口诀)

容器解析三步走:

  1. 找容器 —— Container 是 Prism 内置的“大仓库”
  2. 下订单 —— Resolve<MainWindow>() 告诉仓库要什么
  3. 自动组装 —— 容器检查构造、递归创建依赖、组装好返回来

九、思考题(自检用)

  1. 如果 MainWindow 的构造函数需要 ILogger,但没有注册,会发生什么?
  2. RegisterSingletonRegister 的区别是什么?
  3. 在单元测试中,如何让容器返回一个模拟的 IEventAggregator

学习感悟:刚开始接触 DI 时觉得“绕”,但理解了 Resolve 本质是“自动递归创建依赖”之后,再看 Prism 代码就豁然开朗了。这种“只依赖接口,不依赖具体实现”的设计,让代码变得非常灵活和可测试。