(语法基础)浅谈C#依赖注入
什么是依赖注入?不管是js中的一些前端框架还是,java,C#等中的一些后端语言开发框架中,都会涉及这个的词语:依赖注入,单纯听这个词汇好像很厉害的样子,大部分人都会对未知的事物产生排斥和畏惧,但是其实只要通过简单学习后你会发现其实并没有想象中那么复杂。
1,基本概念
依赖注入?控制反转?
依赖注入是控制反转的一种具体实现方式,那什么又是控制反转:它是面向对象编程中的一种设计原则,其作用主要用来减低计算机代码之间的耦合度,
其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
2,概念理解与运用
如果有一定的面向对象思想其实要理解这一概念的并不算太难,我们常规面向对象最基本的思想就是我们会把某些业务模块需要封装成为一个类,然后在需要的地方去实例化调用,我们不妨举一个生活中的简单栗子,把它体现到代码之中后,看看代码不使用依赖注入和使用依赖注入有什么区别:
如果把我自己比作一个类,假定我这个类里面有工作“Work()”和休息放假“Holiday()”两个函数,然后我在工作和休息两个函数中都会用到我的苹果手机,工作“Work()”函数中会用手机打电话和发邮件,休息“Holiday()”函数会用手机玩游戏、上网还有看视频,我们把手机也看成另一个类,手机类分别实现了:打电话:Call()、发邮件:Eamil()、玩游戏:PlayGame()、上网:Internet()、看视频:VideoPlay()五个函数。代码如下:
public class Person { } public class Me : Person { public void Work() { //…… 省略若干代码 iphone phone = new iphone(); phone.Call(); phone.Eamil(); } public void Holiday() { //…… 省略若干代码 iphone phone = new iphone(); phone.PlayGame(); phone.VideoPlay(); phone.Internet(); } } public class iphone { public void Call() { //…… 省略若干代码 Console.WriteLine($"iphone手机打电话"); } public void Eamil() { //…… 省略若干代码 Console.WriteLine($"iphone手机发送邮件"); } public void PlayGame() { //…… 省略若干代码 Console.WriteLine($"iphone手机玩游戏"); } public void VideoPlay() { //…… 省略若干代码 Console.WriteLine($"iphone手机播放视频"); } public void Internet() { //…… 省略若干代码 Console.WriteLine($"iphone手机上网"); } }
调用代码:
public class Program { public static void Main(string[] args) { int[] Holiday = { 0, 6 }; //获取当前时间 DateTime time = DateTime.Now; //实例化Me对象 Me me = new Me(); if (Holiday.Contains((int)time.DayOfWeek)) { //如果当前时间为周六,周日,执行Holiday me.Holiday(); } else { //如果当前时间不为周六,周日,执行Work me.Work(); } } }
看到这个代码我们觉得很OK,终于可以愉快的工作和休息了,但是好景不长,有一天我从苹果手机换成了华为手机,为了我代码可以正常运行,我必须把所有实例化过iphone类的地方改成实例化华为类,在将来我每换一次手机,我的对应的实例也会跟着改变,代码实例对象的地方存在耦合。所以我们要想办法解耦,怎么处理呢?
public class Person { } public class Me : Person { private phone myphone; public Me(phone _phone) { myphone = _phone; } public void Work() { //…… 省略若干代码 myphone.Call(); myphone.Eamil(); } public void Holiday() { //…… 省略若干代码 myphone.PlayGame(); myphone.VideoPlay(); myphone.Internet(); } } public abstract class phone { public abstract void Call(); public abstract void Eamil(); public abstract void PlayGame(); public abstract void Internet(); public abstract void VideoPlay(); } public class iphone : phone { public override void Call() { //…… 省略若干代码 Console.WriteLine($"iphone手机打电话"); } public override void Eamil() { //…… 省略若干代码 Console.WriteLine($"iphone手机发送邮件"); } public override void PlayGame() { //…… 省略若干代码 Console.WriteLine($"iphone手机玩游戏"); } public override void VideoPlay() { //…… 省略若干代码 Console.WriteLine($"iphone手机播放视频"); } public override void Internet() { //…… 省略若干代码 Console.WriteLine($"iphone手机上网"); } } public class huaweiphone : phone { public override void Call() { //…… 省略若干代码 Console.WriteLine($"华为手机打电话"); } public override void Eamil() { //…… 省略若干代码 Console.WriteLine($"华为手机发送邮件"); } public override void PlayGame() { //…… 省略若干代码 Console.WriteLine($"华为手机玩游戏"); } public override void VideoPlay() { //…… 省略若干代码 Console.WriteLine($"华为手机播放视频"); } public override void Internet() { //…… 省略若干代码 Console.WriteLine($"华为手机上网"); } }
代码标红的地方是主要涉及依赖注入的地方,我们把手机类作为一个父类,然后华为和iPhone都继承此类,然后我们把手机类作为Me类的成员,这样改动后我们的调用代码如下
public class Program { public static void Main(string[] args) { int[] Holiday = { 0, 6 }; //获取当前时间 DateTime time = DateTime.Now; //实例化手机对象 phone phone = new huaweiphone(); //实例化Me对象 Me me = new Me(phone); if (Holiday.Contains((int)time.DayOfWeek)) { //如果当前时间为周六,周日,执行Holiday me.Holiday(); } else { //如果当前时间不为周六,周日,执行Work me.Work(); } } }
代码这样更改之后不管我们要用iPhone类还是华为类还是其他新的手机类去实现我们的Holiday()和Work()中的函数,我们不需要对Me类做任何改动,只需要去改变传输的对象,在调用时灵活的将我们所需要的依赖对象注入到Me类之中,这就就是传说中的依赖注入。
回看前面的例子再用我自己的话来理解依赖注入:就是把依赖部分(代码不可控或者经常变动的耦合的部分)变成一个抽象的成员(类、抽象类或接口),然后根据具体所需要的实例去灵活的注入依赖,来达到控制反转的效果,从而实现代码解耦。
上面这个简单的例子中依赖部分就是iPhone类,华为类这种可能存在变动的类,每种手机具体实现打电话Call(),发邮件Eamil()等功能的方式可能都有差异,我们只能在具体要用到的时候才会知道到底要用哪一种手机,所以在具体调用的时候去注入手机对象是相对灵活而且耦合度低的一种方案。
3,C#常用的依赖注入方式
1,通过构造器进行依赖注入
2,通过属性的访问器进行依赖注入
3,通过接口实现依赖注入
4,通过反射,特性也可以实现依赖注入
我们上面例子是通过构造函数实现的简单的依赖注入,其他几种方式在搞清楚代码基础语法概率之后其实也都差不多,我就不一一举例了,不管用什么方式实现依赖注入,理解其中的基本思想是最重要的。