1、前言
理解这些概念对于我来讲有些很是困难。 但实际上它们很是简单,咱们在平常编码中使用它。
今天,我想谈谈编码中依赖性的问题以及控制反转(IOC)和依赖注入(DI)想要说些什么。 本文面向渴望了解最重要原则,但在实现方面有点困惑的读者。服务器
2、疑问点
1. 什么是控制反转(IOC)?。
2. 什么是依赖注入(DI)?。
3. 实现依赖注入的方法
4. 实施这一原则的优势函数
3、控制反转IOC
经过下面的例子讲解:在咱们大学平常,咱们有时会举办各类活动,有时甚至是无聊的讲座,考虑并回忆大学的平常生活,让咱们尝试将大学和活动与控制反转(IOC)联系起来。测试
public class College { private TechEvents _events = null; public College() { _events = new TechEvents(); } public void GetEvents() { _events.LoadEventDetail(); } } public class TechEvents { public void LoadEventDetail() { Console.WriteLine("Event Details"); } }
上面 College 类的做用是建立 TechEvents 对象。this
假设我有一个类为Colleage,另外一个类为TechEvents。 正如您在上面所看到的,可能会出现许多问题:编码
1. 这两个类彼此紧密耦合。 我不能没有TechEvents的College,由于在College构造器中建立了一个TechEvents对象。
2. 若是我对TechEvents进行任何更改,我须要编译,或者你也能够说更新College类。
3. College 控制 Events 的建立。College 知道有组织的单一 Event。 若是有任何特定 event 像足球活动或Party活动被组织,则须要对 College 类进行更改,由于College直接引用Events。spa
如今我须要以某种方式解决这个问题,不然咱们将没法在大学里举办任何其余活动。code
解决这个问题的方法多是将事件组织的控制权转移到其余地方。咱们称之为控制反转(IOC),将控制权转换为其余实体,而不是直接在 College 组织 Event。什么反转控制原理说?对象
换句话说,主要类不该该具备聚合类的具体实现,而应该依赖于该类的抽象。 College类应该依赖于使用接口或抽象类的TechEvents类抽象。blog
/// <summary> /// 建立一个接口为了实现抽象 /// </summary> public interface IEvent { void LoadEventDetail(); } /// <summary> /// 全部类型事件活动类应该实现 IEvent /// </summary> public class TechEvent : IEvent { public void LoadEventDetail() { Console.WriteLine("Technology Event Details"); } } /// <summary> /// 全部类型事件活动类应该实现 IEvent /// </summary> public class FootballEvent : IEvent { public void LoadEventDetail() { Console.WriteLine("Football Event Details"); } } /// <summary> /// 全部类型事件活动类应该实现 IEvent /// </summary> public class PartyEvent : IEvent { public void LoadEventDetail() { Console.WriteLine("Party Event Details"); } } public class College { private IEvent _events = null; /// <summary> /// College 构造器提示说须要一个活动事件 /// </summary> /// <param name="ie"></param> public College(IEvent ie) { _events = ie; } public void GetEvents() { _events.LoadEventDetail(); } }
4、依赖注入(DI)索引
可使用依赖注入(DI)来完成 IOC 。 它解释了如何将具体实现注入到使用抽象的类中,换句话说就是内部的接口。 依赖注入的主要思想是减小类之间的耦合,并将抽象和具体实现的绑定移出依赖类。
简单来讲,DI就是一个对象如何去知道那些被抽象的其余依赖对象。实现依赖注入主要有4种方法。
一、构造函数注入
public class College { private IEvent _events = null; /// <summary> /// College 构造器提示说须要一个活动事件 /// </summary> /// <param name="ie"></param> public College(IEvent ie) { _events = ie; } public void GetEvents() { _events.LoadEventDetail(); } }
如上所示,事件对象由构造函数注入,使其保持抵耦合。 College类将完成他的工做,若是它想获取与事件相关的详细信息,它将根据他想要调用的事件在构造函数中调用它。
College coll = new College(new FootballEvent());
除了这个优点,另外一个优势是,若是事件有任何变化或添加了更多事件,那么College不须要关心这一点。
二、方法注入
class College { private IEvent _events; public void GetEvent(IEvent myevent) { this._events = myevent; } }
如上所示,我使用GetEvents()方法调用College事件,其中事件类型做为抽象类型的参数传递。 这将帮助我在不影响College 的状况下添加或更改事件,换句话说,二者都是分离的。 这就是我能够调用该方法的方法。
College coll = new College(); coll.GetEvent(new FootballEvent());
三、属性注入
这是最经常使用的方法,咱们经过建立接口类型的属性来注入具体类。
class College { private IEvent _events; public IEvent MyEvent { set { _events = value; } } }
如上所示,MyEvent属性的setter将获取一个具体对象并将其绑定到接口。 个人类与具体的对象低耦合。 如今对任何类型的Event类的任何更改都不会影响个人College类。
College coll = new College(); coll.MyEvent = new FootballEvent();
四、服务器定位注入
服务定位器能够像一个简单的运行时映射器。 这容许在运行时添加代码而无需从新编译应用程序,在某些状况下甚至无需从新启动它。
public class College { private IEvent _events = null; EventLocator el = new EventLocator(); public College(int index) { _events = el.LocateEvent(index); } } public class EventLocator { public IEvent LocateEvent(int index) { if (index == 1) { return new FootballEvent(); } else if(index == 2) { return new PartyEvent(); } else { return new TechEvent(); } } }
在上面的代码片断中,您能够看到 Events 和 College 之间有一个EventLocator类,它能够帮助咱们在不知道具体类型的状况下找到服务。 我只是在构造函数中传递索引值,而构造函数又调用第三方来定位事件并将其返回给构造函数。 所以,对EventLocator的任何更改都不会影响College类。
College coll = new College(1); coll.GetEvents();
实时上述原则的优势:
一、它有助于类的解耦。
二、因为解耦,代码的可重用性增长。
三、改进了代码可维护性和测试。
5、总结
控制反转(IOC)讨论了谁将启动调用,其中依赖注入(DI)讨论一个对象如何经过抽象获取对其余对象的依赖。
这边文章主要引用国外网友的文章,如有以为有不合理之处,能够查看原文 :https://www.c-sharpcorner.com/UploadFile/cda5ba/dependency-injection-di-and-inversion-of-control-ioc/