依賴註入 控制反轉(inversion of control,IOC)是設計模式中非常重要的思想,而依賴註入(dependency injection,DI)是控制反轉思想的一種重要的實現方式。依賴註入簡化了模塊的組裝過程,減小了模塊之間的耦合度,因此.NET Core中大量應用了依賴註入的開發模式 ...
依賴註入
控制反轉(inversion of control,IOC)是設計模式中非常重要的思想,而依賴註入(dependency injection,DI)是控制反轉思想的一種重要的實現方式。依賴註入簡化了模塊的組裝過程,減小了模塊之間的耦合度,因此.NET Core中大量應用了依賴註入的開發模式
控制反轉
控制反轉的目的是將“創建和組裝對象”操作的控制權從業務邏輯轉移到框架中。當我們需要某個類型的對象時,由框架來提供這個對象,我們不需要關註此對象的創建過程
服務定位器
假設框架中有個ServiceLocator的類,通過調用GetService()
就能獲取我們想要的對象。至於對象創建過程我們則不需要關心
IDbConnection conn = ServiceLocator.GetService<IDbConnection>();
依賴註入
public class Demo
{
IDbConnection conn;
public Demo(IDbConnection conn)
{
this.conn = conn;
}
}
系統在創建Demo
類時,自動為conn
賦一個合適的對象。這種框架自動創建對象的動作就叫做註入
.NET Core 依賴註入的基本使用
.NET Core中內置了控制反轉機制,支持依賴註入和服務定位器兩種方式。由於依賴註入是推薦的方式,因此微軟將內置的控制反轉組件命名為DependencyInjection
生命周期
依賴註入框架中註冊的服務有一個重要的概念叫做“生命周期”,一共有三種
- 瞬態(transient)每次被請求的時候都會創建一個新對象
- 範圍(scoped)在給定的範圍內,服務每次被請求都會返回同一個對象
- 單例(singleton)全局共用同一個服務對象
開始使用
安裝依賴包
Microsoft.Extensions.DependencyInjection
創建介面和實現類
public interface ITestService
{
public void Run();
}
public class TestServiceImpl : ITestService
{
public void Run()
{
Console.WriteLine("我是測試實現類");
}
}
註冊服務和獲取服務
ServiceCollection services = new ServiceCollection();
services.AddScoped<ITestService, TestServiceImpl>(); //true,因為在同一範圍內獲取對象所以為true
//services.AddSingleton<ITestService, TestServiceImpl>(); 單例,全局共用一個對象,所以返回true
//services.AddTransient<ITestService, TestServiceImpl>(); 瞬態每次獲取都會創建一個新的對象,返回false
using (ServiceProvider sp = services.BuildServiceProvider())
{
ITestService service1 = sp.GetRequiredService<ITestService>();
ITestService service2 = sp.GetRequiredService<ITestService>();
Console.WriteLine(service1 == service2); //true,因為在同一範圍內獲取對象所以為true
Console.Read();
}
上面通過GetRequiredService方法來獲取ITestServcie對象,很顯然這種屬於服務定位器方式
通過構造函數註入服務
增加服務類
public interface IPayService
{
public void Pay();
}
public class PayServiceImpl : IPayService
{
public void Pay()
{
Console.WriteLine("支付了10元");
}
}
改寫TestServiceImpl類
public class TestServiceImpl : ITestService
{
public IPayService PayService;
public TestServiceImpl(IPayService PayService)
{
this.PayService = PayService;
}
public void Run()
{
PayService.Pay();
}
}
註冊服務和運行
ServiceCollection services = new ServiceCollection();
services.AddScoped<ITestService, TestServiceImpl>();
services.AddScoped<IPayService, PayServiceImpl>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
ITestService service = sp.GetRequiredService<ITestService>();
service.Run();
}
輸出結果:支付了10元
不要在長生命周期的對象中引用比它短的生命周期的對象。比如不能在單例服務中引用範圍服務,否則可能會導致被引用的對象已經釋放或者導致記憶體泄漏