这是一个面向 .NET MAUI 应用的依赖注入(DI)实践指南,帮助开发者正确使用内置 DI 容器管理服务与页面、视图模型。
用途:在 MauiProgram.cs 中注册服务和页面/视图模型,并通过构造函数注入获取依赖,减少服务定位器式写法,提升可测试性与可维护性。
生命周期选择框架:持有共享状态或创建开销大的对象用 AddSingleton;无状态、轻量或按请求创建的对象用 AddTransient;仅当你自行管理 IServiceScope 时才用 AddScoped。MAUI 没有按页面自动创建作用域,直接使用 AddScoped 会静默表现为单例,容易出错。实践建议是页面与视图模型注册为 Transient,共享状态服务(如 IDataService、HttpClient 工厂)注册为 Singleton。单例陷阱示例:把 DetailViewModel 注册为 Singleton 会导致跨页面导航后数据陈旧。
XAML 资源解析与 DI 时机:App.xaml 中的样式、转换器在 InitializeComponent() 期间解析,此时容器尚未完全可用。若资源需要服务,应在 CreateWindow() 中解析,而不是在构造函数里。
未注册页面的静默问题:若页面在 Shell XAML 中使用但未在 builder.Services 注册,MAUI 会走无参构造函数,构造函数注入被静默跳过、依赖为 null 且不抛异常。需注入的页面应始终注册,如 builder.Services.AddTransient<DetailPage>() 与 AddTransient<DetailViewModel>()。
反模式:过度使用服务定位器,将 Handler.MauiContext.Services.GetService 散落在代码中会隐藏依赖、难以测试;应优先构造函数注入,仅在自定义 handler 或平台回调等确实无法使用构造函数注入时显式解析。
平台条件注册陷阱:使用 #if 指令注册平台服务时,要保证接口在所有目标平台都有注册,否则会得到运行时 null,可为未覆盖平台提供 no-op 回退实现。
检查清单:所有需要注入的页面与视图模型都已注册;页面/视图模型为 Transient,共享服务为 Singleton;尽量使用构造函数注入;为需要 mock 的服务定义接口;平台 #if 注册覆盖全部目标平台或提供回退;延迟绑定服务在 CreateWindow() 中解析;仅在手动管理 IServiceScope 时使用 AddScoped。
限制:资料为实践指导,未提供具体包版本、安装命令或官方链接细节;示例中的占位注册代码(如 AddTransient() 无泛型参数)按原文保留,实际使用时需补全服务类型。