一,前言:
C# .net底层倾向于DI而JAVA的Spring倾向于IOC(SpringIOC,怎么实现就不去了解了。DI?DL?),所以学下这块的知识。
二,概念理解:
1.Ioc反转控制思想:
反转控制即把内置对象的控制权反转给一个容器,而应用程序只需要提供对象的类型即可。
①意义:
可以用来减低计算机代码之间的耦合度,是‘面向对象编程’中的一种设计原则。
②举例说明:
当一个类a调用类b的方法时,不再使用new b的方式,而是由父类(容器)new b,通过某种方式使a可以使用b的方法。
③常见的实现方式:
a.依赖注入(Dependency Injection,简称DI):
依赖注入就是将服务注入到使用它的地方。对象只提供普通的方法让容器去决定依赖关系,容器全权负责组件的装配,它会把符合依赖关系的对象通过属性或者是构造子传递给需要的对象。
b.依赖查找(Dependency Lookup):
内置对象通过容器的API来查找自己所依赖的资源和协作对象。这种方式虽然降低了对象间的依赖,但是同时也使用到了容器的API,造成了我们无法在容器外使用和测试对象。 依赖查找是一种更加传统的IoC实现方式。包含等手法。
2.DI依赖注入思想:
①技术描述:
Class A中用到了Class B的对象b,一般情况下,需要在A的代码中显式的new一个B的对象。采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中。
前三种为‘三大依赖注入的方式’(必须掌握的)):
a.构造函数方法注入:Ioc容器会检查被注入对象的构造方法,取得它所需要的依赖对象列表,进而为其注入相应的对象。
b.Setter(属性)注入:对象可以为其依赖对象所对应的属性添加setter方法。就可以通过setter方法将相应的依赖对象设置到被注入的对象中。
c.方法(参数)注入:在A的方法中把B作为参数传递给A,和构造方法相似。
d.基于注解的依赖注入(注解关键字):使用注解标识方法,没学所以不做讲解。
e.接口注入:这个有点迷,不知道我写的对吗。
补充:三种注入方式比较:
接口注入:强制被注入对象实现不必要的接口,带有侵略性。而构造方法注入和setter方式则不需要如此;
构造方法注入:对象在构造完成后即进入就绪状态,不需要其他处理。当依赖对象比较多时构造方法的参数列表会比较长,代码阅读性变差;
setter方法注入:因为属性(+setget方法)可以命名,所以setter方法注入在描述性上比构造方法注入好一点。
三,DI依赖注入代码讲解
0.源代码(类A调用类B):下面都是A与B类高耦合,使用依赖注入就不需要在A中再实例化B,达到解耦的目的。
1 namespace 依赖注入.原写法
2 {
3 // ClassA
4 internal class ClassA
5 {
6 internal void PrintStr()
7 {
8 Console.WriteLine("HelloWorld!您调用了方法A");
9 ClassB classB = new ClassB();
10 classB.PrintStr();
11 }
12 }
13 // ClassB
14 internal class ClassB
15 {
16 internal void PrintStr()
17 {
18 Console.WriteLine("HelloWorld!您调用了方法B");
19 }
20 }
21 }
或者:
1 namespace 依赖注入.原写法2
2 {
3 public class PrintA
4 {
5 PrintB printB;
6
7 /// <summary>
8 /// 构造函数
9 /// </summary>
10 public PrintA()
11 {
12 printB = new PrintB();
13 }
14
15 public void PrintStr()
16 {
17 Console.WriteLine("HelloWorld!您调用了方法A");
18 printB.PrintStr();
19 }
20 }
21
22 public class PrintB
23 {
24 public void PrintStr()
25 {
26 Console.WriteLine("HelloWorld!您调用了方法B");
27 }
28 }
29 }
①构造方法注入(推荐):构造函数注入是最简单的,如下:
1 namespace 依赖注入.原构造函数方法
2 {
3 public class PrintA
4 {
5 PrintB _printB;
6
7 /// <summary>
8 /// 构造函数
9 /// </summary>
10 public PrintA(PrintB printB)
11 {
12 _printB = printB;
13 }
14
15 public void PrintStr()
16 {
17 Console.WriteLine("HelloWorld!您调用了方法A");
18 _printB.PrintStr();
19 }
20 }
21
22 public class PrintB
23 {
24 public void PrintStr()
25 {
26 Console.WriteLine("HelloWorld!您调用了方法B");
27 }
28 }
29 }
// 在其他方法中的调用A
PrintA printA=new PrintA();
printA.PrintStr();
②setter属性注入(推荐):
1 namespace 依赖注入.原setter方法
2 {
3 internal class ClassA
4 {
5 private ClassB _classB;
6 public ClassB classB
7 {
8 get { return _classB; }
9 set { _classB = value; }
10 }
11
12 public void PrintStr()
13 {
14 _classB.PrintStr();
15 }
16
17 }
18
19 internal class ClassB
20 {
21 internal void PrintStr()
22 {
23 Console.WriteLine("HelloWorld!您调用了方法B");
24 }
25 }
26 }
27
28 // 在其他地方调用A:
29 ClassA classA=new ClassA();
30 classA.classB=new ClassB();
31 classA.PrintStr();
③方法注入
1 namespace 依赖注入.作为方法参数传递
2 {
3 internal class ClassA
4 {
5 public void PrintStr(PrintB print)
6 {
7 print.PrintStr();
8 }
9
10 }
11
12
13 internal class PrintB
14 {
15 internal void PrintStr()
16 {
17 Console.WriteLine("HelloWorld!您调用了方法B");
18 }
19 }
20 }
// 外面调用
ClassA classA=new ClassA();
classA.PrintStr(new PrintB());
④接口注入(这个有点迷,不知道我写的对吗,不推荐):
namespace 依赖注入.原接口
{
// 接口
interface IPrint
{
void PrintStr()
{
}
}
// 实现类
public class PrintB : IPrint
{
public void PrintStr()
{
Console.WriteLine("HelloWorld!您调用了方法B");
}
}
}
// 在A类中调用B
IPrint print = new PrintB();
print.PrintStr();
看到这里以为结束了?是不是get不到依赖注入的点,其实上面整个第三块的讲解是错误的(不能说是全部错误,是和正确使用习惯不一致,多是讲解手法存在误导),只是讲解什么是接口、什么是构造方法、什么是setter属性(上面代码请手动敲一下)、什么是方法,开始上手依赖注入。
1.构造方法注入(推荐):
1 namespace 依赖注入.构造函数法注入
2 {
3 internal class ClassA
4 {
5 private IPrint _iPrint;
6
7 internal ClassA(IPrint print)
8 {
9 _iPrint=print;
10 }
11
12 public void PrintStr()
13 {
14 _iPrint.PrintStr();
15 }
16
17 }
18
19 interface IPrint
20 {
21 internal void PrintStr() { }
22 }
23
24 internal class PrintB : IPrint
25 {
26 internal void PrintStr()
27 {
28 Console.WriteLine("HelloWorld!您调用了方法B");
29 }
30 }
31 }
32 // 外面调用
33 ClassA classA=new ClassA(new IPrint());
34 classA.PrintStr();
2.Setter注入示例(推荐):
1 namespace 依赖注入.setter方法注入
2 {
3 internal class ClassA
4 {
5 private IPrint _iPrint;
6 public IPrint iPrint
7 {
8 get { return _iPrint; }
9 set { _iPrint = value; }
10 }
11
12 public void PrintStr()
13 {
14 _iPrint.PrintStr();
15 }
16
17 }
18
19 interface IPrint{
20 internal void PrintStr() { }
21 }
22
23 internal class PrintB:IPrint
24 {
25 internal void PrintStr()
26 {
27 Console.WriteLine("HelloWorld!您调用了方法B");
28 }
29 }
30 }
32 // 外面使用
33 ClassA classA =new ClassA();
34 classA.iPrint=new IPrint();
35 iPrint.PrintStr();
3.方法注入
namespace 依赖注入.作为方法参数传递
{
internal class ClassA
{
public void PrintStr(IPrint print)
{
print.PrintStr();
}
}
interface IPrint
{
internal void PrintStr() { }
}
internal class PrintB : IPrint
{
internal void PrintStr()
{
Console.WriteLine("HelloWorld!您调用了方法B");
}
}
}
// 外面调用
ClassA classA=new ClassA();
classA.PrintStr(new IPrint());
4.接口注入(不推荐):
1 namespace 依赖注入.接口注入
2 {
3 internal class ClassA:ISetService
4 {
5 private IPrint _iPrint;
6
7 internal void SetRunServer(IPrint iPrint)
8 {
9 _iPrint = iPrint;
10 }
11
12 public void PrintStr()
13 {
14
15 _iPrint.PrintStr();
16 }
17 }
18
19 interface ISetService{ // 多加的接口
20 void SetRunServer(IPrint iPrint) { }
21 }
22
23 interface IPrint{
24 internal void PrintStr() { }
25 }
26
27 internal class PrintB:IPrint
28 {
29 internal void PrintStr()
30 {
31 Console.WriteLine("HelloWorld!您调用了方法B");
32 }
33 }
34 }
35 // 在父类中调用时
36 ClassA classA=new ClassA();
37 classA.SetRunServer(IPrint iPrint);
38 classA.PrintStr();
其他:
// .NET Core Web应用程序
services.Configure<Content>(Configuration.GetSection("ContentList")); // 从Content.json配置文件中的ContentList节点匹配到Content对象
作者:꧁执笔小白꧂