前言

最近又在项目中碰到需要将原本单实现的接口改造成多个实现的场景,这里记录一下常见的几种改法。

假设已经存在如下接口ICustomService和其实现CustomService,由于只有一种实现,注入和使用非常容易。

public interface ICustomService
{
void MethodA();
void MethodB();
}
public class CustomService: ICustomService
{
public void MethodA()
{
} public void MethodB()
{
}
} //注入
builder.Services.AddTransient<ICustomService, CustomService>(); //使用
private readonly ICustomService _customService;
public CustomController(ICustomService customService)
{
_customService = customService;
}

现在我们需要增加一种实现。

使用多个接口实现

我们可以将原ICustomService内的方法移到到一个新的基接口,共享出来,需要多少个实现,就创建多少个空接口继承该基接口。

//基接口
public interface ICustomBaseService
{
void MethodA();
void MethodB();
} //多个空接口
public interface ICustomService : ICustomBaseService
{
} public interface ICustomServiceV2 : ICustomBaseService
{
} //第一种实现
public class CustomService: ICustomService
{
public void MethodA()
{
} public void MethodB()
{
}
} //第二种实现
public class CustomServiceV2: ICustomServiceV2
{
public void MethodA()
{
} public void MethodB()
{
}
} //注入
builder.Services.AddTransient<ICustomService, CustomService>();
builder.Services.AddTransient<ICustomServiceV2, CustomServiceV2>(); //使用
private readonly ICustomService _customService;
private readonly ICustomServiceV2 _customServiceV2;
public CustomController(ICustomService customService,ICustomServiceV2 customServiceV2)
{
_customService = customService;
_customServiceV2 = customServiceV2;
}

这种实现方式需要增加了一套空接口做隔离,看似可能比较“浪费”,但后期随着项目的演进,ICustomServiceICustomServiceV2可能会慢慢分化,我们可以很方便的为它们扩充各种独有方法。

使用单接口实现

如果我们确定不要要多个接口,也可以使用下面的单接口实现

public interface ICustomService
{
void MethodA();
void MethodB();
} //第一种实现
public class CustomService: ICustomService
{
public void MethodA()
{
} public void MethodB()
{
}
} //第二种实现
public class CustomServiceV2: ICustomService
{
public void MethodA()
{
} public void MethodB()
{
}
} //注入
builder.Services.AddTransient<ICustomService, CustomService>();
builder.Services.AddTransient<ICustomService, CustomServiceV2>(); //使用
private readonly ICustomService _customService;
private readonly ICustomServiceV2 _customServiceV2;
public CustomController(IEnumerable<ICustomService> customServices)
{
_customService = customServices.ElementAt(0);
_customServiceV2 = customServices.ElementAt(1);
}

从上面代码可以看到,我们是为从接口ICustomService注册两个实现,并从IEnumerable<ICustomService>解析出了这两个实现。这里可能会有两个疑问

  1. 为什么第一个实现CustomService没有被第二个实现CustomServiceV2替换掉?
  2. 为什么可以从IEnumerable<ICustomService>解析到我们需要的服务?

答案在Microsoft.Extensions.DependencyInjection.ServiceDescriptorMicrosoft.Extensions.DependencyInjection.ServiceCollection 这两个类里,进程里,依赖注入的服务,会被添加到ServiceCollection里,ServiceCollection是一组ServiceDescriptor的集合,ServiceDescriptor通过服务类型、实现以及生命周期三个组合在一起构成的标识来确定服务。而ICustomService+CustomService+TransientICustomService+CustomServiceV2+Transient是两个不同的ServiceDescriptor,因此不会被替换。同时服务类型的ServiceDescriptor会被聚合在一起,于是我们可以很方便的从IEnumerable对象中解析出所有的同类型的服务。

总结

本质上,两种方法都是多态性(Polymorphism)的应用,没有优劣之分,根据场景选择合适的写法。

链接

https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection

https://github.com/dotnet/runtime

.NET依赖注入之一个接口多个实现的更多相关文章

  1. DDD实战8_2 利用Unity依赖注入,实现接口对应实现类的可配置

    1.在Util类库下新建DIService类 /// <summary> /// 创建一个类,对应在配置文件中配置的DIServices里面的对象的 key /// </summar ...

  2. JUnit5依赖注入与测试接口

    依赖注入 以前的JUnit的类构造方法和测试方法都是不能有参数的,JUnit Jupiter有一个颠覆性的改进,就是允许它们有入参,这样就能做依赖注入了. 如果你对pytest的fixture有了解的 ...

  3. 解读ASP.NET 5 & MVC6系列(7):依赖注入

    在前面的章节(Middleware章节)中,我们提到了依赖注入功能(Dependency Injection),ASP.NET 5正式将依赖注入进行了全功能的实现,以便开发人员能够开发更具弹性的组件程 ...

  4. 不用Unity库,自己实现.NET轻量级依赖注入

    在面向对象的设计中,依赖注入(IoC)作为一种重要的设计模式,主要用于削减计算机程序的耦合问题,相对于Java中的Spring框架来说,微软企业库中的Unity框架是目前.NET平台中运用比较广泛的依 ...

  5. MVC3+AutoFac实现程序集级别的依赖注入

    1.介绍      所谓程序集级别的依赖注入是指接口和实现的依赖不使用配置文件或硬代码实现(builder.RegisterType<UserInfoService>().As<IU ...

  6. [Castle Windsor]学习依赖注入

    初次尝试使用Castle Windsor实现依赖注入DI,或者叫做控制反转IOC. 参考: https://github.com/castleproject/Windsor/blob/master/d ...

  7. Java Web系列:Spring依赖注入基础

    一.Spring简介 1.Spring简化Java开发 Spring Framework是一个应用框架,框架一般是半成品,我们在框架的基础上可以不用每个项目自己实现架构.基础设施和常用功能性组件,而是 ...

  8. [转]解读ASP.NET 5 & MVC6系列(7):依赖注入

    本文转自:http://www.cnblogs.com/TomXu/p/4496440.html 在前面的章节(Middleware章节)中,我们提到了依赖注入功能(Dependency Inject ...

  9. IOC容器的依赖注入

    1.依赖注入发生的时间 当Spring IoC容器完成了Bean定义资源的定位.载入和解析注册以后,IoC容器中已经管理类Bean定义的相关数据,但是此时IoC容器还没有对所管理的Bean进行依赖注入 ...

  10. 【ASP.NET MVC 学习笔记】- 04 依赖注入(DI)

    本文参考:http://www.cnblogs.com/willick/p/3223042.html 1.在一个类内部,不通过创建对象的实例而能够获得某个实现了公开接口的对象的引用.这种"需 ...

随机推荐

  1. 基于训练和推理场景下的MindStudio高精度对比

    摘要:MindStudio提供精度比对功能,支持Vector比对能力. 本文分享自华为云社区<[MindStudio训练营第一季]MindStudio 高精度对比随笔>,作者:Tianyi ...

  2. golang在win10安装、环境配置 和 goland(IDE开发golang配置)

    前言 本人在使用goland软件开发go时,对于goland软件配置网上资料少,为了方便自己遗忘.也为了希望和我一样的小白能够更好的使用,所以就写下这篇博客,废话不多说开考. 一.查看自己电脑系统版本 ...

  3. ssm——spring整理

    目录 1.概述 2.Spring工厂与IOC 2.1.为什么要有Spring框架 2.2.什么是IOC 2.Spring工厂对实例注入 2.1.使用标签进行注入 2.2.使用注解进行注入 2.2.3. ...

  4. 小样本利器5. 半监督集各家所长:MixMatch,MixText,UDA,FixMatch

    在前面的几个章节中,我们介绍了几种基于不同半监督假设的模型优化方案,包括Mean Teacher等一致性正则约束,FGM等对抗训练,min Entropy等最小熵原则,以及Mixup等增强方案.虽然出 ...

  5. PyTorch如何加速数据并行训练?分布式秘籍大揭秘

    PyTorch 在学术圈里已经成为最为流行的深度学习框架,如何在使用 PyTorch 时实现高效的并行化? 在芯片性能提升有限的今天,分布式训练成为了应对超大规模数据集和模型的主要方法.本文将向你介绍 ...

  6. KMP 与 Z 函数

    \(\text{By DaiRuiChen007}\) 一.KMP 算法 I. 问题描述 在文本串 \(S\) 中找到模式串 \(T\) 的所有出现,其中 \(|S|=n,|T|=m\) II. 前置 ...

  7. .NetCore下基于FreeRedis实现的Redis6.0客户端缓存之缓存键条件优雅过滤

    前言 众所周知内存缓存(MemoryCache)数据是从内存中获取,性能表现上是最优的,但是内存缓存有一个缺点就是不支持分布式,数据在各个部署节点上各存一份,每份缓存的过期时间不一致,会导致幻读等各种 ...

  8. AI换脸实战教学(FaceSwap的使用)---------第二步Tools:处理输入数据集。

    续上篇:https://www.cnblogs.com/techs-wenzhe/p/12936809.html 第一步中已经提取出了源视频的人脸照片以及对应人脸遮罩(landmark以及其他自选遮罩 ...

  9. MySQL 判断语句 条件函数 case when、if、ifnull

    在MySQL中,需要用到条件判断函数,例如 case when.if.ifnull. 一.方法分类 二.具体方法 (1)if if(expr,result_true,result_false) 注意: ...

  10. Java基础学习笔记-Java数据类型转换-(~ ̄▽ ̄)~

    JAVA数据类型和JS数据类型转换不一样,因为它是强类型语言嘛 类型转换规则 不允许数值类型和布尔类型 转换 整型.实型.字符型数据可以混合运算 类型转换分类 自动类型转换-隐式转换 1.整数转换为小 ...