DIP是依赖倒置原则:一种软件架构设计的原则(抽象概念)。依赖于抽象不依赖于细节

控制反转(IoC)Inversion of Control:**传统开发,上端依赖(调用/指定)下端对象,会有依赖,把对下端对象的依赖转移到第三方容器(工厂+配置文件+反射),能够程序拥有更好的扩展性,是DIP的具体实现方式,可以用来减低计算机代码之间的耦合度。

一种软件设计原则,上层对下层的依赖(即底层模块的获得)交给第三方,它将对象的创建和管理权从应用程序代码本身转移到框架或容器中。

依赖(Dependency):就是有联系,表示一个类依赖于另一个类。

依赖倒置原则(DIP):设计模式六大原则之一,是一种软件架构设计原则。

依赖注入 (DI):IoC 的一种具体实现方式,它允许您在运行时为对象提供其所需的依赖项,而不是让对象自行创建或查找这些依赖项。

DI 容器(或 IoC 容器)就是负责管理对象生命周期和依赖关系的工具。在 Prism 中,您可以使用各种 DI 容器,例如 Unity、DryIoc 等。Prism 通过其抽象层,使得您可以在不修改大部分业务代码的情况下切换不同的 DI 容器。

IoC容器:依赖注入的框架,用来映射依赖,管理对象的创建和生存周期。

IOC容器

IOC(Inversion of Control),即“控制反转”,不是什么技术,而是一种设计思想。

依赖就是有联系,有地方使用它就是有依赖它。

IOC中文叫做控制反转:一般开发,上端都依赖下端的对象,类似于在类中new一个其他类对象。这样上端对下端会有很强的依赖。IOC控制反转就是将 对下端对象的依赖 转移到第三方容器,能够使程序拥有更好的扩展性。

IOC就类似一个容器,需要什么对象,就注册创建什么对象

在这里插入图片描述

Prism框架中,框架中最重要的一个组件就是依赖注入框架,这个框架在一定程度上能够通过一个容器去管理整个框架中所有类的对象及生命周期,并且在引用的时候只需要通过注入接口框架就能够自动根据接口类型找到特定的实例,这个会省掉大量的创建对象操作,而且在在软件设计过程中通过IOC容器实现依赖注入能够最大程度上实现最终的控制反转,从而保证软件设计的时候巨大灵活性和扩展性。

说到IOC这里要提到依赖倒置原则DIP:系统架构时,高层模块不该依赖于底层模块,二者通过抽象来依赖,依赖抽象,而不是具体对象。

依赖倒置原则(DIP)

高层模块不依赖低层次模块的细节,高层次就是不依赖细节而是依赖抽象(不依赖具体的类,而是依赖于接口

    interface IModuleBase//接口
    {
        void Init();//方法,没有方法体
    }
    public class ModuleA : IModuleBase
    {
        public void Init()//实现方法体
        {
            Console.WriteLine("ModuleA Init");
        }
    }
    public class ModuleB :IModuleBase
    {
        public void Init()//实现方法体
        {
            Console.WriteLine("ModuleB Init");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            IModuleBase aTest = new ModuleA();
            IModuleBase bTest = new ModuleB();
            aTest.Init();
            bTest.Init();
        }
    }

依赖注入(DI)

DI 即为依赖注入(Dependency Injection):

是实现IOC的手段和方法,就是能做到构造某个对象时,将依赖的对象自动初始化并注入 :

三种注入方式

  1. 构造函数注入
  2. 属性注入
  3. 方法注入(按时间顺序):

构造函数注入用的最多,默认找参数最多的构造函数,可以不用特性,可以去掉对容器的依赖

  • *Unity容器:**是微软推出的IOC框架,使用这个框架,可以实现AOP面向切面编程,便于代码的后期维护,此外,这套框架还自带单例模式,可以提高程序的运行效率。

在这里插入图片描述

一般来说,服务类型多定义为接口
在这里插入图片描述

IConfig的服务通过构造函数来注入
在这里插入图片描述

在这里插入图片描述

不用关心config哪来的,框架给我提供了一个能够读取配置的接口,写就行了,框架提供,我们不用管。下面两个服务怎么来的我不用管,注入就行了。

在这里插入图片描述

在这里插入图片描述

生命周期

.NET 依赖注入组件采用了生命周期的方式,来管理它所提供的服务实例。

所谓的生命周期,就是指由依赖注入组件创建出来的服务实例,可以存活多久。

生命周期有三种模式:瞬时(Transient)、作用域(Scoped)、单例(Singleton)。

我们在注册服务时,必须要指定服务属于哪种生命周期模式,比如刚才的代码示例:

serviceCollection.AddTransient<IAccount, Account>();
serviceCollection.AddScoped<IMessage, Message>();
serviceCollection.AddSingleton<ITool, Tool>();

从注册方法的名称Addxxxx可以看出,注册的服务采用的生命周期模式依次为

「瞬时\作用域\单例」

由于每个服务都可以有多种实现,所以在进行服务注册时,可以为同一个服务注册多个不同的实现类。

虽然可以注册服务的多个实现类,但是GetService方法只能返回其中一个实现类的实例,也就是最后注册的实现类。

如果想要获取某个服务的所有实现,我们可以看这个示例:

public  static  class Sample02
{
    //...
    public  abstract  class  Base { }

    public  class  Account:Base, IAccount{}
    public  class  Message:Base, IMessage{}
    public  class  Tool:Base, ITool{}

    public static void Run()
    {
        // 创建服务集合
        var serviceCollection = new ServiceCollection();
        // 注册服务
        serviceCollection.AddTransient<Base, Account>();
        serviceCollection.AddScoped<Base, Message>();
        serviceCollection.AddSingleton<Base, Tool>();
        // 创建服务提供对象
        var serviceProvider = serviceCollection.BuildServiceProvider();
        // 获取服务集合
        var services = serviceProvider.GetServices<Base>().ToList();
    }
}

示例中添加了一个 Base 抽象类,其它的类型都是 Base 的具体类。

这种抽象类和具体类的关系,类似接口与实现类的关系,所以也可以注册到依赖注入系统中。

使用GetServices 方法可以获取某个服务的类型集合,注意这个 Service 是复数形式。

示例中返回的是一个 Base 类集合,集合的元素就是已注册的 3 个具体类实例。

通过前面的示例,我们可以发现,服务注册的方法有多个,每一个方法对应着不同的生命周期。

什么是生命周期?

简单来说,服务的生命周期代表着每一个服务实例的生存期。

为什么依赖注入系统中的服务实例要定义生命周期呢?

因为依赖注入系统,管理着整个应用的服务实例。

在我们开发应用的时候,肯定用到过单例。被设计为单例模式的对象,在整个应用的生命周期中,有且只有一个。

非单例模式的普通对象,都是随用随建,用完即丢。

既然依赖注入系统管理着整个应用的服务实例,那么不管是高贵的单例对象、还是没有人权普通对象,都是依赖注入系统中的一员。

于是,为了让依赖注入系统分辨出哪个对象属于哪种模式,就有了不同模式的生命周期。

前面我们说过,生命周期的模式有三种:瞬时、作用域和单例

1.瞬时

「瞬时,就是没有生存期。」

也就是说,每次从依赖注入系统中获取瞬时的服务实例时,都会创建一个全新的对象

依赖注入系统中的服务容器不会保存它,也就是没有生存权的普通对象。

2.单例

「单例,就是会一直存在,与应用同寿。」

也就是说,第一次从依赖注入系统中获取单例的服务实例时,才会创建一个全新的对象。

依赖注入系统中的服务容器会保存它,之后的每次使用都是直接从容器中获取它,也就是高贵的单例对象。

3.作用域

「作用域,理解起来没有那么直观,需要结合场景来说明。」

比如,在 ASP.NET 的应用中,每一个来自外部的请求,都可以理解为是一个请求作用域。不同的请求,就是不同的请求作用域。

在同一个请求作用域中,获取作用域模式的服务实例与单例模式的服务实例,具有同样的表现。

也就是说,只有第一次从依赖注入系统中获取服务实例时,才会创建一个全新的对象

依赖注入系统会在服务容器中为该作用域开个单间,单独保存该对象

当请求结束时,请求作用域会被销毁,单间自然也就没了,其中保存的对象也会随之销毁。

所以,在这种模式中生存的对象实例,都只作用于自己的域范围,不同的域不会互相干涉

由此可见,服务一旦有了生命周期,那么依赖注入系统就可以根据需求,来保存和管理它们的实例。

Container(容器)

你可以把它想象成一个智能工厂,专门负责为你“生产”和“组装”各种对象(或者说“组件”)。当你需要一个特定功能的工具(比如一个处理用户注册的服务)时,你不用自己去制作这个工具的所有零件(比如数据库连接、邮件发送器),也不用手动把它们组装起来。你只需要告诉这个“工厂”你需要什么,它就会自动为你完成这些工作。

本质上都是告诉容器:“嘿,我有这个类型的服务,当有人需要它时,你应该提供那个类型的实现。”

这里的Container通常是一个泛指,代表任何依赖注入容器的实例。它是一个变量名,或者一个通用术语,可以在使用任何依赖注入框架(如 Unity, Autofac, Ninject, SimpleInjector 等)时用来指代其容器对象。

容器主要做了什么?
  1. 对象管理: 容器知道如何创建各种对象,什么时候创建,以及在不再需要时如何销毁它们。它就像一个高效的管家,管理着你应用程序中所有重要对象的生命周期。
  2. 依赖组装: 这是容器最主要的功能。当一个对象(比如一个用户服务)需要依赖其他对象(比如一个数据库访问器)才能工作时,容器会自动识别这些依赖,然后把它们“注入”到用户服务中。这样,你的用户服务就不需要自己去查找或创建数据库访问器了。
  3. 降低耦合: 因为你不再需要手动创建和管理对象的依赖,你的代码就变得更加独立。举个例子,如果有一天你决定从MySQL数据库换成PostgreSQL,你只需要告诉容器这个变化,而不需要修改那些使用了数据库访问器的代码。

ContainerLocator

在 Prism 框架中,ContainerLocator(容器定位器)是一个非常重要的概念,它主要用于定位和访问应用程序中已注册的依赖注入容器实例。

ContainerLocator 是 Prism 中用于提供对依赖注入容器的静态全局访问的机制。它简化了在应用程序的各个部分获取和使用容器的过程,同时促进了框架的解耦、模块化和可测试性。它就像一个指路牌,告诉你如何找到并使用应用程序的“服务中心”(即DI容器)。

ContainerLocator 的核心作用可以概括为以下几点:

  1. 全局访问容器
    1. 在 Prism 应用程序的生命周期中,依赖注入容器通常在应用程序启动时进行初始化。ContainerLocator 提供了一个静态的、全局可访问的点,允许应用程序的任何部分在需要时获取到这个已初始化的容器实例。
    2. 这意味着你不需要通过参数传递容器实例,也不需要担心在应用程序的不同部分如何获取到同一个容器。
  2. 解耦与抽象
    1. 通过 ContainerLocator,Prism 框架的各个组件(如模块、服务、视图模型等)可以独立于具体的依赖注入容器实现
    2. 例如,Prism 的某些内部机制需要解析服务或视图,它们会通过 ContainerLocator 获取到容器,然后使用容器来解析所需的类型,而不需要知道底层使用的是 Unity 还是其他容器。这大大增强了框架的灵活性和可替换性。
  3. 支持模块化
    1. Prism 应用程序通常由多个独立的模块组成。每个模块可能需要访问主应用程序的容器来注册自己的服务,或者从容器中解析依赖项。
    2. ContainerLocator 使得模块能够方便地访问到主容器,从而实现模块间的协作和依赖解析。
  4. 在某些Prism组件中的使用
    1. Prism 的许多内置服务和组件,例如 ModuleManagerRegionManager 以及某些事件聚合器(EventAggregator)的实现,都会在内部使用 ContainerLocator 来获取或解析它们所依赖的服务。

IContainerExtension

在 Prism 框架中,IContainerExtension 是一个核心接口,它定义了 Prism 框架与任何依赖注入 (DI) 容器进行交互的标准契约。简单来说,它是 Prism 自身对 DI 容器功能的一套抽象和封装。

位于 Prism.Ioc 命名空间下

为什么需要 IContainerExtension?

Prism 的一个设计原则是容器无关性 (Container Agnosticism)。这意味着 Prism 框架的核心代码不应该直接依赖于任何特定的 DI 容器(比如 Unity、Autofac、Ninject 等)。这样设计有以下几个好处:

  1. 灵活性:开发者可以根据自己的偏好或项目需求,自由选择使用任何支持的 DI 容器,而不需要修改 Prism 相关的代码。
  2. **可维护性:**如果未来需要更换 DI 容器,只需要替换实现 IContainerExtension 的特定容器适配器,而不需要大规模重构整个应用程序。
  3. 可测试性:由于代码与具体容器解耦,更容易进行单元测试和集成测试。

为了实现这种容器无关性,Prism 定义了 IContainerExtension 接口。Prism 框架内部(比如 ModuleManagerRegionManagerEventAggregator 等组件)在需要注册服务或解析依赖时,都只会与 IContainerExtension 接口打交道,而不会直接调用具体容器(如 UnityContainer)的方法。

IContainerExtension 的主要职责

IContainerExtension 接口通常会定义以下几类核心方法:

  1. 注册 (Registration)
    1. 注册类型到容器中。例如,将一个接口映射到一个具体的实现,或者注册一个单例。
    2. 示例方法可能包括:Register<TFrom, TTo>()RegisterSingleton<TInterface, TImplementation>() 等。
  2. 解析 (Resolution)
    1. 从容器中获取已注册类型的实例。
    2. 示例方法可能包括:Resolve<T>()Resolve(Type type) 等。
  3. 创建子容器或作用域 (Optional)
    1. 某些 DI 容器支持创建嵌套或子容器,IContainerExtension 也可能提供相应的方法。

IContainerExtension 的实现

每个支持的 DI 容器都需要提供一个实现了 IContainerExtension 接口的类。例如:

  • 对于 Unity 容器,Prism 提供了一个 UnityContainerExtension 类。这个类内部封装了一个 UnityContainer 实例,并将 IContainerExtension 接口的所有调用转发给底层的 UnityContainer。
  • 对于 Autofac 容器,Prism 社区或第三方会提供一个类似AutofacContainerExtension 类。

为什么不直接与IContainer交互

根本原因在于:为了实现真正的容器无关性(Container Agnosticism)和未来扩展性。

1. 并非所有 DI 容器都有一个名为 IContainer 的公共接口

首先要明确的是,DI 容器并没有一个统一的、.NET 标准库中定义的 IContainer 接口

  • Unity 有它自己的具体类 UnityContainer
  • AutofacIContainer 接口,但这个 IContainer 是 Autofac 自己定义的,不是 .NET 标准库中的。
  • NinjectIKernel 接口。
  • Microsoft.Extensions.DependencyInjection (ASP.NET Core 默认的 DI) 使用 IServiceProvider 接口。

每个容器都有自己的核心接口或类名来表示“容器”本身。如果 Prism 直接依赖一个名为 IContainer 的接口,那么这个 IContainer 接口本身就必须由 Prism 自己定义,或者依赖于某个第三方库,这会:

  • 引入额外的依赖:Prism 将不得不强制开发者使用一个特定的抽象接口,即使他们的容器不直接实现它。
  • 限制灵活性:如果 Prism 真的定义了一个 IContainer 接口,那么每个容器为了与 Prism 集成,就必须实现这个接口,这可能与容器自身的设计理念不符,或者导致适配的复杂性。

2. IContainerExtension 提供了 Prism 所需的“额外功能”或“特定行为”

Prism 框架在管理模块、区域(Regions)和事件时,除了基本的类型注册和解析之外,可能还需要 DI 容器提供一些特定的行为或事件,而这些行为不一定存在于所有容器的原始核心接口中。

例如:

  • 容器生命周期事件:Prism 可能需要在容器注册或解析特定类型时执行一些操作。
  • 特定的注册约定:Prism 可能有自己的一套注册约定(例如,自动注册视图模型)。
  • 集成 Prism 特有的服务:Prism 内部的某些服务可能需要访问容器,并以 Prism 特有的方式进行交互。

IContainerExtension 接口就是 Prism 定义了自己需要容器提供的功能集合。每个具体的 *ContainerExtension(如 UnityContainerExtension)在实现这个接口时,会负责将 Prism 定义的这些抽象功能,映射到其底层具体容器的相应功能上。这样,Prism 就能确保无论使用哪个容器,它都能获得其正常运行所需的全部功能。

3. ContainerLocator 的设计也需要一个统一的抽象

ContainerLocator 是一个静态的全局访问点,它需要一个统一的类型来返回或设置。IContainerExtension 正好提供了这个统一的抽象。

当你在 ContainerLocator.SetContainerExtension(() => new UnityContainerExtension(container)); 时,你传入的是一个实现了 IContainerExtension 接口的对象。

这样,ContainerLocator.Current.Container 属性就能返回一个统一的 IContainerExtension 类型,而它的调用者(Prism 内部或其他模块)就无需知道底层是 UnityContainer 还是 AutofacContainer

总结

IContainerExtensionPrism 框架具体依赖注入容器之间的“契约”或“适配层”。它定义了一套标准的操作,使得 Prism 可以在不知道底层是哪个具体容器的情况下,依然能够执行注册和解析等 DI 操作。这确保了 Prism 应用程序的高度灵活性、可维护性和模块化。

可以把它想象成一个通用插座。不同的电器(具体容器,如 Unity、Autofac)都有不同的插头,但是只要它们能适配这个万能适配器接口(IContainerExtension,Prism 框架就能“通电”并正常工作。确保了无论你的DI容器插头是什么形状,只要它能适配IContainerExtension 这个“插座”,就能和 Prism 这个“电器”正常工作。


如何使用

以Unity举例子

使用Unity来管理对象与对象之间的关系可以分为以下几步:

  • 创建一个UnityContainer对象
  • 通过UnityContainer对象的RegisterType方法来注册对象与对象之间的关系
  • 通过UnityContainer对象的Resolve方法来获取指定对象关联的对象
 IUnityContainer container = new UnityContainer();

后面我们用到的方法,其实大多数是IUnityContainer接口的拓展方法。

Register and Resolve

public interface ICar //接口
{
    int Run();
}

public class BMW : ICar //BMW类实现接口
{
    private int _miles = 0;

    public int Run()
    {
       return ++_miles;
    }
}

public class Ford : ICar//Ford类实现接口
{
    private int _miles = 0;

    public int Run()
    {
        return ++_miles;
    }
}

public class Audi : ICar//Audi类实现接口
{
   private int _miles = 0;

   public int Run()
   {
        return ++_miles;
   }
}

public class Driver
{
    private ICar _car = null;
    public Driver(ICar car)//注入进去,通过构造函数传递值
    {
       _car = car;
    }
}

public void RunCar()
{
    Console.WriteLine($"Running {_car.GetType().Name} - {_car.Run()} mile ");}
}

container.RegisterType<ICar, BMW>(); //注册对象与对象之间的关系
Driver driver = container.Resolve<Driver>(); // 获取指定对象关联的对象
driver.RunCar();
output:Running BMW - 1 mile

我们可以看到Driver类依赖ICar接口,我们一个简单的做法就是实现一个ICar接口的对象,通过构造函数传递给Driver1)

Multiple Registration

container.RegisterType<ICar, BMW>();
container.RegisterType<ICar, Ford>();
Driver driver = container.Resolve<Driver>();
driver.RunCar();

output:Running Ford - 1 mile

同一个接口ICar注册多次的时候,以最后一次注册的类型为准,前面均会被覆盖。

Unity容器

using Unity;
using Unity.Lifetime;

public class UnityConfig
{
    public static void RegisterComponents()
    {
        // 1. 创建一个 Unity 容器实例
        var container = new UnityContainer();

        // 2. 注册 ILogger 接口到 ConsoleLogger 实现
        // 默认是瞬态(Transient),每次解析都会创建新实例
        container.RegisterType<ILogger, ConsoleLogger>();

        // 3. 注册 UserService
        // Unity 会自动解析 UserService 构造函数中需要的 ILogger
        container.RegisterType<UserService>();

        // 4. 也可以注册为单例(Singleton),整个应用程序只创建一个实例
        // container.RegisterType<ILogger, ConsoleLogger>(new ContainerControlledLifetimeManager());

        // 5. 注册带名称的实现
        container.RegisterType<ILogger, FileLogger>("fileLogger");

        // --- 解析示例 ---
        var userService = container.Resolve<UserService>();
        userService.RegisterUser("Alice"); // 会使用 ConsoleLogger

        var fileLogger = container.Resolve<ILogger>("fileLogger");
        fileLogger.Log("This message goes to file."); // 会使用 FileLogger

        // 也可以直接解析已注册的类型
        var loggerInstance = container.Resolve<ILogger>();
        loggerInstance.Log("Directly resolved default logger.");
    }
}

// 使用 new UnityContainer() 创建容器实例。
// RegisterType<TInterface, TImplementation>() 将接口映射到实现。
// RegisterType<TService>() 可以直接注册具体类,Unity 会尝试解析其依赖。
// RegisterType(new ContainerControlledLifetimeManager()) 用于注册为单例。
// RegisterType<TInterface, TImplementation>(string name) 用于注册命名实例。
// Resolve<T>() 或 Resolve<T>(string name) 用于从容器中获取实例。

UnityContainer

UnityContainerUnity 依赖注入框架具体实现依赖注入容器的类。它是一个具体的类型,直接代表了 Unity 框架提供的容器功能。

当你看到代码中出现 new UnityContainer() 或者 UnityContainer container; 时,它明确指的就是 Unity 框架的容器实例。

仅限于Unity框架,是Unity框架提供的DI容器.是Container的一种特定的类型和实现

与container不同,container通常是一个泛指,代表任何依赖注入容器的实例

UnityContainer container 的一种特定类型和实现。所有 UnityContainer 都是 container(依赖注入容器),但并非所有 container 都是 UnityContainer

UnityContainerExtension

IContainerExtension 提供了规范,而 UnityContainerExtension 则提供了遵循这个规范的具体实现。

IContainerExtension = 容器操作的“做什么”(What):定义了容器应该提供哪些功能。

UnityContainerExtension = 容器操作的“怎么做”(How):具体告诉 Prism 如何使用 Unity 容器来完成这些功能。

DryIoc容器

using DryIoc;

public class DryIocConfig
{
    public static void RegisterComponents()
    {
        // 1. 创建一个 DryIoc 容器实例
        var container = new Container();

        // 2. 注册 ILogger 接口到 ConsoleLogger 实现
        // 默认是 Transient(瞬态),每次解析都会创建新实例
        container.Register<ILogger, ConsoleLogger>();

        // 3. 注册 UserService
        // DryIoc 会自动解析 UserService 构造函数中需要的 ILogger
        container.Register<UserService>();

        // 4. 注册为单例(Singleton),整个应用程序只创建一个实例
        // container.Register<ILogger, ConsoleLogger>(Reuse.Singleton);

        // 5. 注册带名称的实现
        container.Register<ILogger, FileLogger>(serviceKey: "fileLogger");

        // 6. 也可以使用工厂方法进行更灵活的注册
        // container.Register<ILogger>(() => new ConsoleLogger());

        // --- 解析示例 ---
        var userService = container.Resolve<UserService>();
        userService.RegisterUser("Bob"); // 会使用 ConsoleLogger

        var fileLogger = container.Resolve<ILogger>("fileLogger");
        fileLogger.Log("This message goes to file (DryIoc)."); // 会使用 FileLogger

        var loggerInstance = container.Resolve<ILogger>();
        loggerInstance.Log("DryIoc: Directly resolved default logger.");
    }
}

//Register<TService, TImplementation>() 将服务类型映射到实现类型。
//Register<TService>() 可以直接注册具体类。
//Register(Reuse.Singleton) 用于注册为单例。
//Register(serviceKey: "name") 用于注册命名实例。
//Register(() => new TImplementation()) 可以使用 Lambda 表达式作为工厂方法。
//Resolve<T>() 或 Resolve<T>(serviceKey: "name") 用于从容器中获取实例。

DryIoc.Container

这是 DryIoc 框架中具体的依赖注入容器实现类

它实现了 DryIoc.IContainer 接口,并提供了所有 DryIoc 特有的注册(Register)和解析(Resolve)服务的方法。

当你使用 DryIoc 时,你会创建 new Container() 来开始配置你的 DI 容器。

特性 / 框架Unity DI 框架DryIoc DI 框架
容器实现类UnityContainerDryIoc.Container
核心接口IUnityContainerDryIoc.IContainer
创建实例new UnityContainer()new Container()
注册服务container.RegisterTypecontainer.Register
解析服务container.Resolve()container.Resolve()

DryIoc.Container 类: 这是 DryIoc.IContainer 接口的默认实现类。当你创建一个 DryIoc 容器实例时,通常就是这个类的实例。

在 Prism 的 PrismApplication 派生类中,你可以通过 this.Container 属性访问到 Prism 内部使用的 DI 容器实例。如果你使用的是 DryIoc 作为底层容器,那么 this.Container 实际上就是 DryIoc 的 Container 实例。

示例: 如果你需要执行一些 DryIoc 特有的高级配置,或者直接使用 DryIoc 的 API 而不是 Prism 提供的抽象(例如 IContainerRegistryIContainerProvider),你可以在 ConfigureContainer 方法中将 this.Container 强制转换为 DryIoc.IContainer 类型。

using DryIoc; // 确保引用 DryIoc 命名空间
using Prism.DryIoc; // 确保引用 Prism.DryIoc 命名空间
using Prism.Ioc;
using Prism.Modularity;

public class App : PrismApplication
{
    // ... 其他 PrismApplication 的方法 ...

    protected override void ConfigureContainer()
    {
        base.ConfigureContainer(); // 总是调用基类实现

        // 访问 DryIoc 容器实例,进行 DryIoc 特有的配置
        var dryIocContainer = (IContainer)this.Container;

        // 示例:DryIoc 特有的注册方式,例如注册一个委托工厂
        dryIocContainer.Register<ILogger, ConsoleLogger>();

        // 示例:设置 DryIoc 的一些高级规则
        dryIocContainer.Rules = Rules.Default.With(Made.Of.Constructor(FactoryMethod.ConstructorWithResolvableArguments));

        // 示例:注册一个带有特定生命周期的服务
        dryIocContainer.Register<IMyService, MyServiceImplementation>(Reuse.Singleton);
    }
}

何时使用 DryIoc.IContainer:

  • 当你需要执行 IContainerRegistryIContainerProvider 无法满足的 DryIoc 特定高级配置时(例如,设置容器规则、注册带有 DryIoc 特有生命周期的服务、使用 DryIoc 的特定工厂方法等)。
  • 当你在集成一些只支持 DryIoc 原生 API 的第三方库时。

DryIocContainerExtension

在 Prism.DryIoc 扩展库中,IContainerExtension 的 DryIoc 实现类是 DryIocContainerExtension。

  • DryIocContainerExtension: 这个类实现了 IContainerExtension 接口,它将 Prism 对 DI 容器的操作(如 Register、Resolve)转发给底层的 DryIoc IContainer 实例。

你通常不会直接与 DryIocContainerExtension 类打交道,而是通过 Prism 提供的抽象接口来操作它:

  • IContainerRegistry: 这是 IContainerExtension 的注册部分,用于注册类型和实例。当你在 RegisterTypes 方法中收到 IContainerRegistry 参数时,它实际上就是 DryIocContainerExtension 的一个封装。
  • IContainerProvider: 这是 IContainerExtension 的解析部分,用于解析服务实例。当你通过 this.Container 属性获取 IContainerProvider 实例并调用 Resolve 方法时,底层也是通过 DryIocContainerExtension 来调用 DryIoc 容器的解析方法。
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    // containerRegistry 实际上是 DryIocContainerExtension 的一个实例(或其派生类)
    containerRegistry.RegisterSingleton<IMyService, MyServiceImplementation>();
    containerRegistry.RegisterForNavigation<MainView, MainViewModel>();
}

protected override void OnInitialized()
{
    // this.Container (作为 IContainerProvider) 也是 DryIocContainerExtension 的一个实例(或其派生类)
    Container.Resolve<IRegionManager>().RequestNavigate("ContentRegion", "WelcomeView");
}

在上述代码中,containerRegistry 和 Container (作为 IContainerProvider)都是 Prism 抽象的体现。它们的底层实现是 DryIocContainerExtension,负责与实际的 DryIoc.IContainer 实例进行交互。

何时使用 IContainerExtension

(通常是通过 IContainerRegistry 和 IContainerProvider 抽象):

  • 在你的应用程序中进行标准的服务注册和解析时。
  • 当你希望你的代码与具体的 DI 容器解耦,以便将来可以轻松切换到其他容器时。
  • 在遵循 Prism 最佳实践,优先使用构造函数注入时,IContainerExtension(或其提供的抽象)会在幕后默默工作。

更多推荐