【WPF】Prism学习(七)

Prism Dependency Injection

1.注册类型(Registering Types)

1.1. Prism中的服务生命周期

  • Transient(瞬态):每次请求服务或类型时,都会获得一个新的实例。
  • Singleton(单例):每次请求服务或类型时,都会获得同一个实例。
  • Scoped(作用域):在每个容器作用域中获得一个新的实例,但在特定的容器作用域内保持同一个实例。

1.2. Prism中的默认作用域

  • Prism默认不使用作用域,除了在Prism.Maui中,它会为每个页面创建一个作用域。这种作用域用于INavigationServiceIPageDialogServiceIDialogService等服务。

1.3. 与ASP.NET Core的比较

  • 如果你熟悉ASP.NET Core,你可能知道三种基本的依赖注册类型:Transients、Singletons和Scoped Services。与Web环境中许多服务围绕用户请求进行作用域划分不同,桌面和移动应用通常只处理单个用户。因此,我们需要决定服务是作为整个应用中重复使用的单个实例,还是每次请求时创建新实例,并在完成后由垃圾回收器清理内存。

1.4. Prism对命名服务注册的要求

  • Prism严格要求使用命名服务注册。这使得Prism能够注册页面以供导航,并在之后根据URI片段(如MyMasterDetailPage/NavigationPage/ViewA)解析它。任何不支持命名服务的依赖注入容器都不能被Prism团队官方实现。

2.注册瞬态(Transient)服务

瞬态服务是指每次请求服务或类型时都会创建一个新的实例。在Prism中,如果你期望每次创建服务时都生成一个新的实例,你可以通过调用Register方法并提供服务类型和服务实现类型来注册这样的服务。除非在某些情况下,直接注册具体类型(concrete type)可能是合适的。

csharp 复制代码
// Where it will be appropriate to use FooService as a concrete type
containerRegistry.Register<FooService>();

containerRegistry.Register<IBarService, BarService>();

具体来说,代码示例展示了两种注册瞬态服务的方式:

  1. 如果FooService是一个具体类型,并且你希望每次请求FooService时都创建一个新的实例,你可以直接使用containerRegistry.Register<FooService>();来注册。

  2. 如果IBarService是一个接口,而BarService是实现这个接口的具体类,你希望每次请求IBarService时都创建一个新的BarService实例,你可以使用containerRegistry.Register<IBarService, BarService>();来注册。

这两种方式都是在告诉Prism的依赖注入容器,当应用中需要FooServiceIBarService时,应该每次都创建一个新的实例,而不是重用已有的实例。这种方式适用于那些不需要在应用的生命周期内保持状态的服务。

3.注册单例(Singleton)服务

  1. 单例服务的定义:在应用程序中,有些服务会被多次使用。如果每次都创建一个新的实例,这将不利于内存管理。因此,更好的做法是将这些服务注册为单例,这样它们就可以在整个应用程序中被重复使用。

  2. 单例服务的内存管理:单例服务在注册时并不会立即创建实例,它们不会开始占用内存,直到应用程序第一次解析(请求)这些服务时才会创建实例。

  3. 注册单例服务的代码示例

csharp 复制代码
// Where it will be appropriate to use FooService as a concrete type
containerRegistry.RegisterSingleton<FooService>();

containerRegistry.RegisterSingleton<IBarService, BarService>();
  • containerRegistry.RegisterSingleton<FooService>();:这行代码将FooService注册为一个单例服务。这意味着在整个应用程序中,FooService只会有一个实例。
  • containerRegistry.RegisterSingleton<IBarService, BarService>();:这行代码将接口IBarService和它的实现BarService注册为一个单例服务。这样,任何时候请求IBarService,都会返回同一个BarService实例。

在Prism框架中,对于那些需要在整个应用程序中保持状态或频繁使用的服务,注册为单例是一种有效的做法,它有助于优化内存使用和提高应用程序性能。

4.注册服务实例

  1. 注册服务实例 :通常情况下,我们会通过提供服务类型和服务实现类型来注册单例。但是,有时候我们可能需要直接创建一个服务实例,并将其注册为特定的服务。例如,如果我们有一个接口IFoo和一个实现FooImplementation,我们可以直接创建FooImplementation的实例,并将其注册为IFoo的服务实例。

    csharp 复制代码
    containerRegistry.RegisterInstance<IFoo>(new FooImplementation());
  2. 使用插件中的当前实例 :有时候,我们可能想要注册来自某个插件的当前实例。以James Montemagno的MonkeyCache插件为例,我们首先设置Barrel.ApplicationId为一个唯一的名称,然后注册Barrel.Current作为IBarrel的服务实例。

    csharp 复制代码
    Barrel.ApplicationId = "your_unique_name_here";
    containerRegistry.RegisterInstance<IBarrel>(Barrel.Current);

在Prism框架中,除了通过服务类型和服务实现类型注册服务外,还可以直接注册服务实例,这在集成第三方插件或需要直接控制服务实例时非常有用。

5.检查一个服务是否已经被注册

csharp 复制代码
if (containerRegistry.IsRegistered<ISomeService>())
{
    // Do something...
}
  1. 检查服务是否注册

    • 在编写Prism模块或插件时,开发者可能需要检查某个服务是否已经被注册到依赖注入容器中。这可以通过containerRegistry.IsRegistered<ISomeService>()方法来实现,其中ISomeService是你想要检查的服务接口。
  2. 基于服务注册状态执行操作

    • 如果服务已经被注册,你可以执行一些特定的操作。例如,如果IsRegistered<ISomeService>()返回true,你可以在大括号{}中编写相应的代码来处理这种情况。
  3. 注意事项

    • 如果你在Prism模块中有一个对特定服务的硬依赖,你应该通过构造函数注入该服务。这样做的好处是,如果在模块初始化时缺少该服务类型,将会抛出异常。这有助于在开发过程中及早发现问题。
    • 只有当你的意图是注册一个默认实现时,才应该使用IsRegistered来检查服务是否已经注册。这意味着,如果服务尚未注册,你可以使用IsRegistered检查后,注册一个默认的服务实现。

6.延迟解析(Lazy Resolution)服务

  1. 延迟解析服务 :在Prism框架中,你可以像这样注册服务:containerRegistry.Register<IFoo, Foo>()。有些开发者可能希望节省内存,延迟加载服务,可以将其作为Func<IFoo>Lazy<IFoo>来实现。Prism 8原生支持这种功能。要实现这一点,你只需要在你的ViewModel或Service中添加相应的参数,如下所示:

    csharp 复制代码
    public class ViewAViewModel
    {
        public ViewAViewModel(Func<IFoo> fooFactory, Lazy<IBar> lazyBar)
        {
        }
    }
  2. 注意服务注册类型 :当你使用单例服务时,通常不建议使用Lazy<T>Func<T>进行解析。例如,IEventAggregator是一个单例服务,这意味着整个应用程序中只使用一个事件聚合器实例。如果你使用Lazy<T>Func<T>,最终可能会使用更多的内存,并且可能会因为延迟解析而影响性能,而不是直接请求服务。

这段内容强调了在Prism框架中实现延迟加载服务的方法,以及在使用单例服务时应避免使用Lazy<T>Func<T>,以节省内存和提高性能。

7.全部解析(Resolve All)

  1. Resolve All(全部解析):有些开发者可能需要注册同一个服务契约的多个实现,并期望在需要时能够解析出所有这些实现。这是一种常见的用例,例如Shiny框架在其一些委托接口中使用了这种模式。

  2. 构建模块化代码:通过这种方式,开发者可以构建更模块化的代码,因为可以对同一个事件做出响应,但是每次只处理一小部分(bite sized chunks)。

  3. 注册过程 :对于这种特性,注册过程中没有什么特别的步骤。开发者只需要在构造函数中注入IEnumerable<T>接口即可。例如,如果你有一个IFoo服务接口,并且有多个实现,你可以这样注入:

    csharp 复制代码
    public class SomeService
    {
        public SomeService(IEnumerable<IFoo> fooCollection)
        {
            // 这里的fooCollection将包含所有IFoo的实现
        }
    }
  4. 支持情况:目前,只有DryIoc依赖注入容器支持这个特性。对于使用Unity容器的开发者来说,一旦Unity容器发布6.0版本,这个特性可能会变得可用。

相关链接

相关推荐
爱喝水的鱼丶9 分钟前
SAP-ABAP:SAP 简单报表输出开发系列(共6篇)第三篇:SAP ALV 报表样式定制:字段布局与交互功能配置
服务器·开发语言·学习·交互·sap·abap
很楠爱上14 分钟前
Node.js 模块化学习笔记
笔记·学习·node.js
毕竟是shy哥24 分钟前
CLIP:从自然语言监督中学习可迁移的视觉模型
学习
川石课堂软件测试28 分钟前
作为一名测试工程师如何学习Kubernetes(k8s)技能
学习·测试工具·容器·职场和发展·kubernetes·测试用例·harmonyos
tryqaaa_37 分钟前
学习日志(五)【php反序列化全加例题】【pop链,字符逃逸,session,伪协议】
android·学习·php·web·pop·session
li星野43 分钟前
FastAPI 参数详解:路径参数、查询参数与请求体 —— 从入门到实战
服务器·学习·fastapi
承渊政道1 小时前
【MySQL数据库学习】(MySQL数据类型)
数据库·学习·mysql·ubuntu·bash·数据库开发·数据库系统
WangX-西石油1 小时前
DVWA靶场Low级别Brute Force学习
学习·web安全·网络安全
段一凡-华北理工大学1 小时前
工业领域的Hadoop架构学习~系列文章08:Flink流处理引擎
人工智能·hadoop·学习·架构·flink·高炉炼铁·高炉炼铁智能化
海鸥-w1 小时前
前端学习python第三天笔记整理(list 列表,str字符串,tuple元组,set集合,dect,函数,类型注解)
前端·python·学习