1 定義
依賴注入(Dependency Injection),簡稱DI,類之間的依賴關系由容器來負責。簡單來講a依賴b,但a不創建(或銷毀)b,僅使用b,b的創建(或銷毀)交給容器。
2 例子
為了把DI講清楚,我們需要舉一個簡單例子。例子足夠小,希望讓你能直觀的了解DI而不會陷入真實示例的泥潭。
例子:小明要殺怪,那小明拿什么武器殺怪呢?可以用刀、也可以用拳頭、斧子等。
首先,我們創建一個演員類,名字叫“小明”,具有殺怪功能。
namespace NoInjection.ConsoleApp
{
public class Actor
{
private string name = "小明";
public void Kill()
{
var knife = new Knife();
knife.Kill(name);
}
}
}
然后,我們再創建一個武器-刀類,具有殺怪功能。
using System;
namespace NoInjection.ConsoleApp
{
public class Knife
{
public void Kill(string name)
{
Console.WriteLine($"{name}用刀殺怪");
}
}
}
最后,我們客戶端調用演員類,執行殺怪功能。
using System;
namespace NoInjection.ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var actor = new Actor();
actor.Kill();
Console.ReadKey();
}
}
}
讓我們來看看輸出結果:
小明用刀殺怪
通過這個例子我們可以看到,Actor類依賴Knife類,在Actor中創建Knife,執行Knife.Kill方法。我們可以回顧一下DI的定義,a依賴b,但a不創建(或銷毀)b,僅使用b,顯然這個不符合DI做法。
DI下面我們詳細說說DI的幾種形式。
3 形式
3.1 構造函數注入
首先,我們在Actor通過構造函數傳入Knife。
namespace ConstructorInjection.ConsoleApp
{
public class Actor
{
private string name = "小明";
private Knife knife;
public Actor(Knife knife)
{
this.knife = knife;
}
public void Kill()
{
knife.Kill(name);
}
}
}
然后,Knife類不需要變化。
using System;
namespace ConstructorInjection.ConsoleApp
{
public class Knife
{
public void Kill(string name)
{
Console.WriteLine($"{name}用刀殺怪");
}
}
}
最后,我們客戶端來創建Actor和Knife,然后在Actor通過構造函數傳入Knife。
using System;
namespace ConstructorInjection.ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var knife = new Knife();
var actor = new Actor(knife);
actor.Kill();
Console.ReadKey();
}
}
}
讓我們來看看輸出結果:
小明用刀殺怪
這個例子我們可以看到,Actor類依賴Knife類,但在Actor不創建Knife,而是通過構造函數傳入Knife。
3.2 Setter注入
首先,我們在Actor類創建Knife屬性。
namespace SetterInjection.ConsoleApp
{
public class Actor
{
private string name = "小明";
private Knife knife;
public Knife Knife
{
set
{
this.knife = value;
}
get
{
return this.knife;
}
}
public void Kill()
{
knife.Kill(name);
}
}
}
然后,Knife類不需要變化。
using System;
namespace SetterInjection.ConsoleApp
{
public class Knife
{
public void Kill(string name)
{
Console.WriteLine($"{name}用刀殺怪");
}
}
}
最后,我們客戶端來創建Actor和Knife,然后在Actor通過屬性傳入Knife。
using System;
namespace SetterInjection.ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var knife = new Knife();
var actor = new Actor();
actor.Knife = knife;
actor.Kill();
Console.ReadKey();
}
}
}
讓我們來看看輸出結果:
小明用刀殺怪
這個例子我們可以看到,Actor類依賴Knife類,但在Actor不創建Knife,而是通過屬性傳入Knife。
3.3 接口注入
首先,我們在Actor類創建Knife屬性並繼承IActor
namespace InterfaceInjection.ConsoleApp
{
interface IActor
{
Knife Knife { set; get; }
void Kill();
}
}
namespace InterfaceInjection.ConsoleApp
{
public class Actor: IActor
{
private string name = "小明";
private Knife knife;
public Knife Knife
{
set
{
this.knife = value;
}
get
{
return this.knife;
}
}
public void Kill()
{
knife.Kill(name);
}
}
}
然后,Knife類不需要變化。
using System;
namespace InterfaceInjection.ConsoleApp
{
public class Knife
{
public void Kill(string name)
{
Console.WriteLine($"{name}用刀殺怪");
}
}
}
最后,我們客戶端來創建Actor和Knife,然后在Actor通過屬性傳入Knife。
using System;
namespace InterfaceInjection.ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var knife = new Knife();
IActor actor = new Actor();
actor.Knife = knife;
actor.Kill();
Console.ReadKey();
}
}
}
接口注入方式我理解了也不是很透,感覺跟Setter注入沒有什么大的差別,只是增加了一個接口定義。