前言

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

假设已经存在如下接口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. MasaFramework -- 领域驱动设计

    概念 什么是领域驱动设计 领域驱动的主要思想是, 利用确定的业务模型来指导业务与应用的设计和实现.主张开发人员与业务人员持续地沟通和模型的持续迭代,从而保证业务模型与代码的一致性,实现有效管理业务的复 ...

  2. 在Maven中出现javax.mail中文乱码问题解决记录

    学习Java时,看廖雪峰大神文章使用了javax.mail来发送SMTP邮件.在加入中文时,发现收到的邮件里中文都是乱码. 按照一般经验,多半是编码的问题.然而在代码中,会涉及到编码的地方已经全部设置 ...

  3. 说说真实Java项目的开发流程,以及面试前的项目准备说辞

    介绍项目是必不可少的Java面试环节,求职者需要借此证明自己真实Java项目的经验,如果再做的好的话,需要借此展开自己的亮点说辞. 不过之前如果只有学习项目经验,比如是自己跑通一个项目,或者是在培训班 ...

  4. MySQL5.7兼容5.6

    MySQL5.7兼容5.6配置----MySQL5.7以上版本数据库兼容MySQL5.5-5.6版本数据库 手动安装MySQL 8.0/5.7 需要修改配置兼容 ,修改后需要重启mysql服务 (建议 ...

  5. 【深入浅出Spring原理及实战】「源码原理实战」从底层角度去分析研究PropertySourcesPlaceholderConfigurer的原理及实战注入机制

    Spring提供配置解析功能 主要有一下xml文件占位符解析和Java的属性@Value的占位符解析配置这两种场景进行分析和实现解析,如下面两种案例. xml文件的占位符解析配置 <bean i ...

  6. 12、HSSFWorkbook实现多张sheet导出

    转载自 一.封装一个通用的装载数据的实体类: import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsCon ...

  7. 在生产中部署 ES2015+ 代码

    大多数 Web 开发人员都喜欢编写具有所有最新语言特性的 JavaScript--async/await.类.箭头函数等.然而,尽管事实上所有现代浏览器都可以运行 ES2015+ 代码并原生支持我刚才 ...

  8. Codeforces Round #842 (Div. 2) A-D

    比赛链接 A 题意 给一个数 \(k\) 找到最大的 \(x\) ,满足 \(1 \leq x < k\) 且 \(x!+(x-1)!\) 是 \(k\) 的倍数. 题解 知识点:数学. 猜测 ...

  9. Java面试的一些面试题

    ​ 10<<2=? tip:10的二进制为1010,左移两位即:101000,换算为十进制为2的5次方加上2的三次方等于40 答:40 1.dr-xr-xr-r解释一下权限的含义 tip: ...

  10. [Untiy]贪吃蛇大作战(四)——游戏主界面

    游戏主界面: 由于这个场景比较复杂,需要分几个部分实现: 1.游戏背景 首先我们的游戏场景上包括了一个大的背景图片,之外再包围一个红色的区域.中间的区域才是可活动的区域,周围通过碰撞检测盒来检测是否有 ...