一、引言
IoC-Invertion of Control,即控制反轉,是一種程式設計思想,
先初步了解幾個概念:
依賴(Dependency):就是有聯系,表示一個類依賴于另一個類,
依賴倒置原則(DIP):設計模式六大原則之一,是一種軟體架構設計原則,
控制反轉(IoC):一種軟體設計原則,上層對下層的依賴(即底層模塊的獲得)交給第三方,
依賴注入(DI):實作IoC的一種方式、手段,
IoC容器:依賴注入的框架,用來映射依賴,管理物件的創建和生存周期,
二、依賴
依賴就是有聯系,有地方使用它就是有依賴它,下面看一個簡單的示例:

class Program { class BMW { public string Show() { return "寶馬"; } } class ChinesePeople { private BMW bmw = new BMW(); public void Run() { Console.WriteLine($"今天開{bmw.Show()}上班"); } } static void Main(string[] args) { ChinesePeople people = new ChinesePeople(); BMW bmw = new BMW(); people.Run(); Console.Read(); } }View Code
上面中國人開著寶馬去上班,客戶端有使用中國人、寶馬汽車兩個物件,中國人中有使用物件寶馬汽車,我們可以從中找到三個依賴關系:
客戶端依賴物件ChinesePeople;
客戶端依賴物件BMW;
ChinesePeople依賴物件BMW;
三、依賴倒置原則
過些日子來了新需求,中國人不僅要開寶馬去上班,還要開奔馳去上班,如果按照上面直接依賴關系的方式去做,我們就需要修改ChinesePeople類,讓它實作一個引數為寶馬的多載方法Run(),顯然這樣不是好的設計,我們總不能每次新增一種汽車(即修改下層模塊)都要去修改ChinesePeople類吧(相對于汽車為上層模塊),太麻煩了,,,
先簡單分析一下,耦合關系就是依賴關系,如果依賴關系很重,牽一發而動全身,將很難維護擴展,耦合關系越少,系統會越穩定,因此要較少依賴,
定義:
A.高層模塊不應依賴于底層模塊,兩者應該依賴于抽象,
B.抽象不應該依賴于細節,細節應該依賴于抽象,

在這個圖中,我們發現高層模塊定義介面,將不直接依賴于下層模塊,下層模塊負責實作高層模塊定義的介面,下面看一下示例:
class Program { interface ICar { string Show(); } class BMW : ICar { public string Show() { return "寶馬"; } } class BenZ : ICar { public string Show() { return "奔馳"; } } interface IPeople { void Run(ICar car); } class ChinesePeople : IPeople { public void Run(ICar car) { Console.WriteLine($"今天開{car.Show()}上班"); } } static void Main(string[] args) { ICar bmw = new BMW(); ICar benz = new BenZ(); IPeople people = new ChinesePeople(); people.Run(bmw); people.Run(benz); Console.Read(); } }View Code
運行結果如下:

分析:上面代碼中,ChinesePeople類不再依賴于具體的汽車,而是依賴于汽車的抽象,這樣使得不管換什么樣的汽車品牌,中國人都是可以開著去上班的,而且不需要修改ChinesePeople類,想一下,這樣是不是挺好的,我們可以得出:上層不再依賴細節,相比面向實作,面向介面較好,因為抽象相比細節要更穩定,
四、控制反轉
上面示例中,我們實作了具體的人和具體的汽車的隔離,具體人只和汽車的介面有關,但是Program中Main方法里的具體物件寫死了,控制權變小,當我要修改美國人開著福特去上班時,就不得不要去修改代碼,那怎么把控制權轉移呢?
下面看一個簡單的示例(請先添加System.Configuration參考):
interface ICar { string Show(); }ICar.cs
interface IPeople { void Run(ICar car); }IPeople.cs
class BMW : ICar { public string Show() { return "寶馬"; } }BMW.cs
class ChinesePeople : IPeople { public void Run(ICar car) { Console.WriteLine($"今天開{car.Show()}上班"); } }ChinesePeople.cs
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> </startup> <appSettings> <add key="People" value="LinkTo.Test.ConsoleIoC.ChinesePeople,LinkTo.Test.ConsoleIoC"/> <add key="Car" value="LinkTo.Test.ConsoleIoC.BMW,LinkTo.Test.ConsoleIoC"/> </appSettings> </configuration>App.config
class Program { static void Main(string[] args) { #region 反射+組態檔實作Ioc string people = ConfigurationManager.AppSettings["People"]; string car = ConfigurationManager.AppSettings["Car"]; Assembly assemblyPeople = Assembly.Load(people.Split(',')[1]); Assembly assemblyCar = Assembly.Load(car.Split(',')[1]); Type typePeople = assemblyPeople.GetType(people.Split(',')[0]); Type typeCar = assemblyPeople.GetType(car.Split(',')[0]); IPeople ipeople = (IPeople)Activator.CreateInstance(typePeople); ICar icar = (ICar)Activator.CreateInstance(typeCar); ipeople.Run(icar); Console.Read(); #endregion } }Program.cs
上面代碼中,我們使用反射+組態檔的方式,將物件創建的控制權轉移到了組態檔,這就是所謂的控制反轉,
分析:控制反轉是將物件創建的控制權交給了第三方,可以是IoC容器,它就相當于簡單工廠,我們要什么物件,工廠就給我們什么物件,這樣依賴關系就變了,它們(人和車)都依賴于IoC容器,通過IoC容器建立它們之間的依賴關系,(依賴物件不再直接通過new來獲取)
運行結果如下:

五、依賴注入
上面說到的控制反轉,我們了解到是將控制權轉移,這是我們的目的,組態檔+反射是一種實作,而依賴注入則提供的是一種思想,或者說是實作IoC的手段,
依賴注入是將物件的創建和系結轉移到被依賴物件的外部來實作,一般使用哪些方法來實作呢?
方法一:建構式注入
class ChinesePeopleConstructor { private readonly ICar _car; //依賴注入:建構式注入 public ChinesePeopleConstructor(ICar car) { _car = car; } public void Run() { Console.WriteLine($"今天開{_car.Show()}上班"); } }ChinesePeopleConstructor.cs
class Program { static void Main(string[] args) { #region 依賴注入:建構式注入 ICar bmw = new BMW(); ChinesePeopleConstructor people = new ChinesePeopleConstructor(bmw); people.Run(); Console.Read(); #endregion } }Program.cs
方法二:屬性注入
class ChinesePeopleProperty { //依賴注入:屬性注入 public ICar Car { get; set; } public void Run() { Console.WriteLine($"今天開{Car.Show()}上班"); } }ChinesePeopleProperty.cs
class Program { static void Main(string[] args) { #region 依賴注入:屬性注入 ICar bmw = new BMW(); ChinesePeopleProperty people = new ChinesePeopleProperty { Car = bmw }; people.Run(); Console.Read(); #endregion } }Program.cs
方法三:介面注入
interface IDependent { void SetDependent(ICar icar); }IDependent.cs
class ChinesePeopleInterface : IDependent { private ICar _car; //依賴注入:介面注入 public void SetDependent(ICar car) { _car = car; } public void Run() { Console.WriteLine($"今天開{_car.Show()}上班"); } }ChinesePeopleInterface.cs
class Program { static void Main(string[] args) { #region 依賴注入:介面注入 ICar bmw = new BMW(); ChinesePeopleInterface people = new ChinesePeopleInterface(); people.SetDependent(bmw); people.Run(); Console.Read(); #endregion } }Program.cs
六、IoC容器
IoC容器是一個DI框架,主要功能有一下幾點:
A.動態創建、注入依賴物件;
B.管理物件生命周期;
C.映射依賴關系;
常見的IoC容器:Spring.NET,Castle Windsor, Ninject,Autofac,Unity,,,
6.1、Unity容器使用
在上一篇《C# AOP學習筆記》的【使用EntLib\PIAB Unity實作AOP(帶配置)】中,已經使用Unity容器實作了IoC,讓我們再來看看組態檔:

假如只需要IoC不需要AOP,container是這樣子的:
<configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/> </configSections> <unity> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/> <containers> <container name="IoCContainer"> <!--注冊匹配規則:前面是完整型別名稱,后面是所在的dll名稱,--> <register type="LinkTo.Test.ConsoleAop.UnityConfigAOP.IUserProcessor,LinkTo.Test.ConsoleAop" mapTo="LinkTo.Test.ConsoleAop.UnityConfigAOP.UserProcessor,LinkTo.Test.ConsoleAop"></register> </container> </containers> </unity> </configuration>View Code
從IoC的注冊匹配規則可以看出,前面是完整型別名稱,后面是所在的dll,這個就比較厲害了,假如一個系統有擴展的功能或者個性要求,只需配置使用新的dll即可,原有系統不需要改代碼,這樣,除了很好地符合了"開閉原則"外,對于構建一個可配置可擴展的系統,是一個非常厲害的利器,
參考自:
https://www.cnblogs.com/jdzhang/p/7104351.html
推薦博文:
基于介面設計三層架構
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/59606.html
標籤:C#
