当前位置: 首页 > article >正文

【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)可能是合适的。

// 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. 注册单例服务的代码示例

// 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的服务实例。

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

    Barrel.ApplicationId = "your_unique_name_here";
    containerRegistry.RegisterInstance<IBarrel>(Barrel.Current);
    

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

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

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中添加相应的参数,如下所示:

    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服务接口,并且有多个实现,你可以这样注入:

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

相关链接

  • 介绍(Introduction)
  • 命令(Commands)
    • 命令(Commanding)
    • 复合命令(Composite Commands)
    • 异步命令(Async Commands)
    • 错误处理(Error Handling)
  • 依赖注入(Dependency Injection)
    • 依赖注入(Dependency Injection)
    • 注册类型(Registering Types)

http://www.kler.cn/a/401133.html

相关文章:

  • 【Vue笔记】基于vue3 + element-plus + el-dialog封装一个自定义的dialog弹出窗口组件
  • SpringBoot:不支持发行版本17超详细解决办法
  • Figma中文网:UI设计师的新资源宝库
  • Qt中实现旋转动画效果
  • Siglus引擎 Unpack | 未完待续
  • .NET 简介
  • 《Camera-free Image to PanoramaGeneration with Diffusion Model》论文解析——CamFreeDiff
  • 图像编辑一些概念:Image Reconstruction与Image Re-generation
  • chatgpt训练需要什么样的gpu硬件
  • 得物彩虹桥架构演进之路-负载均衡篇
  • 6 设计模式原则之单一职责原则
  • 深入浅出 Spring Cloud 微服务:架构、核心组件与应用实践
  • NLP论文速读(EMNLP 2024)|动态奖励与提示优化来帮助语言模型的进行自我对齐
  • Java 设计模式 详解
  • AI工具百宝箱|任意选择与Chatgpt、gemini、Claude等主流模型聊天的Anychat,等你来体验!
  • RabbitMQ实战启程:从配置到故障排查的实战处理(下)
  • 高级java每日一道面试题-2024年11月14日-基本篇-static都有哪些用法?
  • Linux之vim全选,全部复制,全部删除
  • 【H2O2|全栈】JS进阶知识(五)ES6(1)
  • 记录下,用油猴Tampermonkey监听所有请求,绕过seesion
  • Jmeter进阶篇(26)杀掉Tomcat的几种方法
  • 国产三维CAD 2025新动向:推进MBD模式,联通企业设计-制造数据
  • 【数据结构】七种常用排序总结
  • 阿里云ElasticSearch跨集群备份恢复实践
  • Cloud Native 云原生后端的开发注意事项
  • Vue3.5正式上线,父传子props用法更丝滑简洁