什么是依赖注入
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注入没有什么大的差别,只是增加了一个接口定义。