前言 最近又在項目中碰到需要將原本單實現的介面改造成多個實現的場景,這裡記錄一下常見的幾種改法。 假設已經存在如下介面ICustomService和其實現CustomService,由於只有一種實現,註入和使用非常容易。 public interface ICustomService { void ...
前言
最近又在項目中碰到需要將原本單實現的介面改造成多個實現的場景,這裡記錄一下常見的幾種改法。
假設已經存在如下介面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;
}
這種實現方式需要增加了一套空介面做隔離,看似可能比較“浪費”,但後期隨著項目的演進,ICustomService
和ICustomServiceV2
可能會慢慢分化,我們可以很方便的為它們擴充各種獨有方法。
使用單介面實現
如果我們確定不要要多個介面,也可以使用下麵的單介面實現
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 ICustomService _customServiceV2;
public CustomController(IEnumerable<ICustomService> customServices)
{
_customService = customServices.ElementAt(0);
_customServiceV2 = customServices.ElementAt(1);
}
從上面代碼可以看到,我們是為從介面ICustomService
註冊兩個實現,並從IEnumerable<ICustomService>
解析出了這兩個實現。這裡可能會有兩個疑問
- 為什麼第一個實現
CustomService
沒有被第二個實現CustomServiceV2
替換掉? - 為什麼可以從
IEnumerable<ICustomService>
解析到我們需要的服務?
答案在Microsoft.Extensions.DependencyInjection.ServiceDescriptor 和 Microsoft.Extensions.DependencyInjection.ServiceCollection 這兩個類里,進程里,依賴註入的服務,會被添加到ServiceCollection
里,ServiceCollection
是一組ServiceDescriptor
的集合,ServiceDescriptor
通過服務類型、實現以及生命周期三個組合在一起構成的標識來確定服務。而ICustomService+CustomService+Transient
和ICustomService+CustomServiceV2+Transient
是兩個不同的ServiceDescriptor
,因此不會被替換。同時服務類型的ServiceDescriptor
會被聚合在一起,於是我們可以很方便的從IEnumerable對象中解析出所有的同類型的服務。
總結
本質上,兩種方法都是多態性(Polymorphism)的應用,沒有優劣之分,根據場景選擇合適的寫法。
鏈接
https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection
https://github.com/dotnet/runtime
作者: 幾秋
出處: https://www.cnblogs.com/netry/p/net-dependency-injection-multiple-implementations.html
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。