跳到主要内容
版本:2.1

依赖注入容器(IOC)

定义

命名空间:TouchSocket.Core
程序集:TouchSocket.Core.dll

一、说明

所谓依赖注入,是指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入。通俗来讲,就是把有依赖关系的类放到容器中,然后在我们需要这些类时,容器自动解析出这些类的实例。依赖注入最大的好处时实现类的解耦,利于程序拓展、单元测试、自动化模拟测试等。依赖注入的英文为:Dependency Injection,简称 DI。(说明来自网络)

TouchSocket内置了Container容器。只需要引入TouchSocket.Core即可使用。

二、特点

  • 支持构造函数、属性、方法三种注入方式,可以选择其中部分生效。
  • 支持SingletonTransient两种生命周期。
  • 支持单接口,多实现注入。
  • 支持当获取类型是可实例类型时,即使不注册,也能成功构造。
  • 支持默认参数注入。
  • 支持构建参数注入。
  • 支持标签参数注入。
  • 支持泛型注入。
  • 支持Object注入。
  • 支持源生成注入,全面接入AOT模式。

三、注入方式

3.1 构造函数注入

【定义类型】

class MyClass1
{

}

class MyClass2
{
public MyClass2(MyClass1 myClass1)
{
this.MyClass1 = myClass1;
}

public MyClass1 MyClass1 { get; }
}

【注册和获取】

/// <summary>
/// 构造函数注入
/// </summary>
static void ConstructorInject()
{
var container = GetContainer();
container.RegisterSingleton<MyClass1>();
container.RegisterSingleton<MyClass2>();

var myClass1 = container.Resolve<MyClass1>();
var myClass2 = container.Resolve<MyClass2>();

Console.WriteLine(MethodBase.GetCurrentMethod().Name);
}

3.2 属性注入

使用DependencyParamterInject,或者DependencyInject标记属性,即可注入。

【定义类型】

class MyClass3
{
/// <summary>
/// 直接按类型,默认方式获取
/// </summary>
[DependencyInject]
public MyClass1 MyClass1 { get; set; }

/// <summary>
/// 获得指定类型的对象,然后赋值到object
/// </summary>
[DependencyInject(typeof(MyClass2))]
public object MyClass2 { get; set; }

/// <summary>
/// 按照类型+Key获取
/// </summary>
[DependencyParamterInject("key")]
public MyClass1 KeyMyClass1 { get; set; }
}

【注册和获取】

static void PropertyInject()
{
var container = GetContainer();
container.RegisterSingleton<MyClass1>();
container.RegisterSingleton<MyClass1>("key");
container.RegisterSingleton<MyClass2>();

container.RegisterSingleton<MyClass3>();

var myClass3 = container.Resolve<MyClass3>();
Console.WriteLine(MethodBase.GetCurrentMethod().Name);
}
提示

DependencyInject特性中,Type和Key,可以同时使用,也可以只使用其中一个。

3.3 方法注入

使用DependencyInject标记属性,即可对方法注入。

【定义类型】

class MyClass4
{
public MyClass1 MyClass1 { get;private set; }


[DependencyInject]
public void MethodInject(MyClass1 myClass1)
{
this.MyClass1 = myClass1;
}
}

【注册和获取】

static void MethodInject()
{
var container = GetContainer();
container.RegisterSingleton<MyClass1>();
container.RegisterSingleton<MyClass4>();

var myClass4= container.Resolve<MyClass4>();
Console.WriteLine(MethodBase.GetCurrentMethod().Name);
}
提示

DependencyInject特性,也可以在方法注入时,对参数进行使用。

3.4 注入筛选

对于一个类,默认情况下,会支持构造函数属性方法三种注入方式。但是,当明确知道该类型仅会使用其中部分方式注入时,可以设置注入类型,以此节约性能。

/// <summary>
/// 让MyClass1仅支持构造函数和属性注入
/// </summary>
[DependencyType(DependencyType.Constructor | DependencyType.Property)]
class MyClass1
{

}
备注

Constructor构造函数配置不管配不配置,默认都是支持的。

3.5 默认参数实例化

当一个类,被瞬时注入后,在获取其实例时,可以按照实际情况重新获得其实际值。

例如:对于MyClass5,我们希望在瞬时注入后,获取其实例时,我们可以手动的重新指定ab以及MyClass1的值。

class MyClass5
{
public MyClass5(int a, string b, MyClass1 myClass1)
{
this.A = a;
this.B = b;
this.MyClass1 = myClass1;
}

public int A { get; }
public string B { get; }
public MyClass1 MyClass1 { get; }
}

那么我们可以这样实现。

var container = GetContainer();
container.RegisterSingleton<MyClass1>();
container.RegisterTransient<MyClass5>();//默认参数只在瞬时生命有效

var myClass5_1 = container.Resolve<MyClass5>(new object[] { 10, "hello" });
var myClass5_2 = container.Resolve<MyClass5>(new object[] { 20, "hi" });
var myClass5_3 = container.Resolve<MyClass5>(new object[] { 20, "hi", new MyClass1() });

//以下结果为true,因为在构建时并未覆盖MyClass1,所以MyClass1只能从容器获得,而MyClass1在容器是以单例注册,所以相同
var b1 = myClass5_1.MyClass1 == myClass5_2.MyClass1;

//以下结果为false,因为在构建时覆盖MyClass1,所以MyClass1是从ps参数直接传递的,所以不相同
var b2 = myClass5_1.MyClass1 == myClass5_3.MyClass1;
备注

默认参数实例化,仅在瞬时注入周期中适用。

四、源生成IOC容器

使用源生成IOC容器,能实现100%代码静态生成。能够有效解决运行时代码效率问题和AOT反射问题。

然后声明自己的容器,遵循如下:

  1. 必须继承ManualContainer
  2. 必须使用partial修饰为部分类。
  3. 必须添加特性GeneratorContainer
 [GeneratorContainer]
partial class MyContainer : ManualContainer
{

}

4.1 注册类型

注册类型,可以直接添加特性即可:

[AutoInjectForSingleton]//将声明的类型直接标识为单例注入
class MyClass12
{

}

[AutoInjectForTransient]//将声明的类型直接标识为瞬态注入
class MyClass13
{

}

4.2 注册现有类型

现有类型已经完成声明,所以无法直接添加特性。所以只能将特性添加在ManualContainer的继承类上。

[AddSingletonInject(typeof(IInterface10), typeof(MyClass10))]//直接添加现有类型为单例
[AddTransientInject(typeof(IInterface11), typeof(MyClass11))]//直接添加现有类型为瞬态
[GeneratorContainer]
partial class MyContainer : ManualContainer
{

}
提示

源生成IOC容器,同样支持属性、方法、Key等注册配置,使用规则和第三节一致。

五、生命周期

生命周期是对注入构造的实例的有效性而言的。TouchSocket支持两种生命周期。

  • Singleton:单例注入,当注入,并且实例化以后,全局唯一实例。
  • Transient:瞬时注入,每次获取的实例都是新实例。

对于这两种,熟悉IOC的同学,相信都知道到。

六、使用ServiceCollection

请安装NuGet包:TouchSocket.Core.DependencyInjection

然后直接使用AspNetCoreContainer替代Container

static IContainer GetContainer()
{
//return new Container();//默认IOC容器

return new AspNetCoreContainer(new ServiceCollection());//使用Aspnetcore的容器
}

Config使用

var config=new TouchSocketConfig()//载入配置
.UseAspNetCoreContainer(new ServiceCollection());//ServiceCollection可以使用现有的

本文示例Demo