一. 溫故而知新html
在開始學習Unity框架以前,有必要溫習一下 【依賴倒置原則】和【手寫IOC】, 由於咱們框架代碼的構建都是基於【依賴倒置原則】的,而Unity框架的核心思想就是IOC和DI,因此有必要了解經過【反射+工廠+配置文件】來實現手寫IOC的原理。android
廢很少說,簡單提煉一下依賴原則的核心理念:面向接口編程。上層(UI層)不直接依賴於下層,而是依賴於下層對應的接口,下層去實現該接口。數據庫
經典案例:業務層鏈接數據庫,依賴接口,能夠實現不改代碼輕鬆切換數據庫的目的。編程
手動建立對象的三個階段:直接new實例化→面向接口編程→將建立對象的交給工廠【手寫IOC】。app
1 #region 手動建立對象幾個階段 2 { 3 Console.WriteLine("--------------一.複習依賴倒置原則和手寫IOC(簡單工廠+反射+配置文件)----------------"); 4 //1. 直接建立(須要添加對Service層的引用) 5 Console.WriteLine("--------------1. 直接建立----------------"); 6 AndroidPhone androidPhone1 = new AndroidPhone(); 7 androidPhone1.Call(); 8 9 //2. 利用接口進行改造(須要添加對Service和Interface層的引用) 10 Console.WriteLine("--------------2. 利用接口進行改造----------------"); 11 IPhone androidPhone2 = new AndroidPhone(); 12 androidPhone2.Call(); 13 14 //3. 手寫IOC(只須要添加Interface層的引用,但須要把Service層生成的DLL拷貝到該項目bin下) 15 Console.WriteLine("--------------3. 利用接口進行改造----------------"); 16 IPhone androidPhone3 = ObjectFactory.CreateObject<IPhone>(); 17 androidPhone3.Call(); 18 19 //總結:第三個階段基本上已是咱們本身寫容器的最高層次 20 //下面咱們將藉助微軟的第三方容器Unity來建立 21 22 } 23 #endregion
依賴倒置原則詳見:http://www.cnblogs.com/yaopengfei/p/7101347.html框架
手寫IOC詳見:http://www.cnblogs.com/yaopengfei/p/6891286.htmliphone
二. Unity深刻淺出ide
(一). Unity介紹函數
1. Unity的獲取方式學習
①:下載網址:http://unity.codeplex.com/
②:經過Nuget獲取 (目前最新版本爲4.0.1)
2. Unity是什麼?
Unity是一個IOC容器,用它能夠來建立對象,代替手寫反射,它能夠實現依賴注入(DI),它出自於微軟。.Net平臺下相似的其它IOC框架還有:Spring.Net、AutoFac、Castle等等。
3. IOC的含義
IOC是一種目標,IOC的含義爲控制反轉,即本來上端(UI層)直接建立或使用的對象,交給第三方容器來裝配和建立。
4. DI的含義
DI是一種手段,DI的含義爲依賴注入,Unity提供三種方式分別是:屬性注入、方法注入、構造函數注入。
(二). 使用Unity來建立對象
1. 建立對象的步驟
①:聲明IUnityContainer容器。
②:經過RegisterType註冊新類型 或者 經過RegisterInstance註冊已經存在的類型。
③:經過Resove解析對象。
特別注意:註冊類型時,能夠指定名稱來註冊,用於同時建立註冊多個類型時,Resove能夠經過指定的註冊名稱來針對指定解析。
2. 代碼說明
①:代碼結構(後面全部的代碼說明都是基於這個結構的)
詳細說明:各個類的內容待寫。
②:直接建立簡單對象(須要添加對Interface層和Service層的引用)
1 { 2 Console.WriteLine("------------------- 二. Unity入門 -------------------"); 3 Console.WriteLine("------------------- 01-直接建立對象 -------------------"); 4 IUnityContainer container = new UnityContainer(); 5 //進行註冊,能夠是: 接口-類、父類-子類、抽象類-子類。 6 //container.RegisterType<IPhone, AndroidPhone>(); 7 //也能夠用實例註冊已經存在的對象 8 AndroidPhone android = new AndroidPhone(); 9 container.RegisterInstance<IPhone>(android); 10 //解析對象 11 IPhone phone = container.Resolve<IPhone>(); 12 phone.Call(); 13 //咱們會發現,雖然註冊了AndroidPhone對象,可是該對象中的三個屬性並無實例化,依舊爲空 14 Console.WriteLine("phone.iHeadphone==null? {0}", phone.iHeadphone == null); 15 Console.WriteLine("phone.iMicrophone==null? {0}", phone.iMicrophone == null); 16 Console.WriteLine("phone.iPower==null? {0}", phone.iPower == null); 17 }
詳解:上述代碼咱們分別經過RegisterType註冊類型、RegisterInstance註冊已經存在的類型,咱們會發現最終生成AndoridPhone中的三個屬性並無被實例化,那麼怎麼實例化裏面的屬性呢?這就涉及到後面的依賴注入了。
③. 指定命名來建立對象(須要添加對Interface層和Service層的引用)
1 { 2 Console.WriteLine("------------------- 03-同時建立多個對象 -------------------"); 3 //能夠經過添加一個參數來區分 4 IUnityContainer container = new UnityContainer(); 5 //註冊類型時(指定命名) 6 container.RegisterType<IPhone, AndroidPhone>(); 7 container.RegisterType<IPhone, ApplePhone>("apple"); 8 container.RegisterType<IPhone, AndroidPhone>("android"); 9 //依賴注入 10 container.RegisterType<IMicrophone, Microphone>(); 11 container.RegisterType<IHeadphone, Headphone>(); 12 container.RegisterType<IPower, Power>(); 13 //解析對象 14 IPhone iphone1 = container.Resolve<IPhone>(); 15 iphone1.Call(); 16 //解析對象(指定命名) 17 IPhone iphone2 = container.Resolve<IPhone>("apple"); 18 iphone2.Call(); 19 IPhone iphone3 = container.Resolve<IPhone>("android"); 20 iphone3.Call(); 21 22 //獲取全部已註冊且指定命名的對象(未指定命名的不能獲取) 23 var list = container.ResolveAll<IPhone>(); 24 25 }
詳解:註冊類型時,若是默認不指定命名,則後面的會覆蓋前面的,解析多個對象時,永遠是最後一個註冊的類型的對象。 因此RegisterType指定命名 、Resolve解析對象指定命名普遍用於同時建立多個對象的狀況。
咱們發現上述代碼有三句是依賴注入的,這個在下面講解。
(三). 依賴注入的三種形式
Unity實現依賴注入有三種形式,分別是構造函數注入、屬性注入、方法注入,分別對應三個特性:【InjectionConstructor】、【Dependency】、【InjectionMethod】,加上這三個特性代表構造函數中、屬性中、方法中須要注入對象,使其實例化。
方式一:屬性注入。
有WinPhone1類,實現了Iphone接口。其中WinPhone1類中的iMicrophone和iHeadphone兩個屬性上分別添加【Dependency】和【Dependency("fk")】,代表該屬性對應的對象須要注入,其中iHeadphone須要Unity容器"指定命名"來註冊類型。
1 public interface IPhone 2 { 3 void Call(); 4 IMicrophone iMicrophone { get; set; } 5 6 IHeadphone iHeadphone { get; set; } 7 8 IPower iPower { get; set; } 9 }
1 /// <summary> 2 /// 測試屬性注入的類 3 /// </summary> 4 public class WinPhone1 : IPhone 5 { 6 [Dependency("fk")] //表示該屬性須要注入,且須要指定名稱 7 public IMicrophone iMicrophone { get; set; } 8 [Dependency] //表示該屬性須要注入 9 public IHeadphone iHeadphone { get; set; } 10 public IPower iPower { get; set; } 11 12 public WinPhone1() 13 { 14 Console.WriteLine("{0}被構造", this.GetType().Name); 15 } 16 17 public void Call() 18 { 19 Console.WriteLine("{0}打電話", this.GetType().Name); ; 20 } 21 }
1 IUnityContainer container = new UnityContainer(); 2 //方式一: 屬性注入 3 Console.WriteLine("----------------------------方式一: 屬性注入 -------------------------------"); 4 container.RegisterType<IPhone, WinPhone1>(); 5 //指定命名注入 6 container.RegisterType<IMicrophone, Microphone>("fk"); 7 //普通注入 8 container.RegisterType<IHeadphone, Headphone>(); 9 IPhone phone1 = container.Resolve<IPhone>(); 10 phone1.Call();
補充說明:咱們發現Microphone和HeadPhone均被構造,說明這兩個類被注入成功。
方式二:方法注入。
有WinPhone2類,實現了Iphone接口。其中在WinPhone2中的方法Init1234方法上加【InjectionMethod】特性,表示該方法中有對象須要注入。
1 public class WinPhone2:IPhone 2 { 3 public IMicrophone iMicrophone { get; set; } 4 public IHeadphone iHeadphone { get; set; } 5 public IPower iPower { get; set; } 6 7 public WinPhone2() 8 { 9 Console.WriteLine("{0}被構造", this.GetType().Name); 10 } 11 12 public void Call() 13 { 14 Console.WriteLine("{0}打電話", this.GetType().Name); ; 15 } 16 17 [InjectionMethod] //方法注入,代表該方法中有對象須要注入 18 public void Init1234(IPower power) 19 { 20 this.iPower = power; 21 } 22 }
1 //方式二:方法注入 2 Console.WriteLine("----------------------------方式二:方法注入 -------------------------------"); 3 container.RegisterType<IPhone, WinPhone2>(); 4 container.RegisterType<IPower, Power>(); 5 IPhone phone2 = container.Resolve<IPhone>(); 6 phone2.Call();
補充說明:咱們發現Power被構造,說明該類被注入成功。
方式三:構造函數注入(又分四種狀況)
A. 默認注入。 自動識別參數數量最多的構造函數,表示該構造函數中有對象須要注入。
B. 指定構造函數。經過在構造函數上加 [InjectionConstructor]特性,表示該構造函數中有對象須要注入。
C. 指定構造函數且指定注入參數的名稱。經過在構造函數上加 [InjectionConstructor]特性,而且在參數上加 [Dependency("fk")] ,表示該構造函數中的該參數中的對象須要注入。
D. 指定構造函數且指定注入參數的值(瞭解便可)。
1 /// <summary> 2 /// 測試構造函數注入(默認方式) 3 /// </summary> 4 public class WinPhone3 : IPhone 5 { 6 public IMicrophone iMicrophone { get; set; } 7 public IHeadphone iHeadphone { get; set; } 8 public IPower iPower { get; set; } 9 10 public WinPhone3(IPower iPower) 11 { 12 Console.WriteLine("{0}被構造", this.GetType().Name); 13 } 14 15 public void Call() 16 { 17 Console.WriteLine("{0}打電話", this.GetType().Name); ; 18 } 19 20 }
1 /// <summary> 2 /// 測試構造函數注入(指定構造函數) 3 /// </summary> 4 public class WinPhone4 : IPhone 5 { 6 public IMicrophone iMicrophone { get; set; } 7 public IHeadphone iHeadphone { get; set; } 8 public IPower iPower { get; set; } 9 public WinPhone4() 10 { 11 12 } 13 [InjectionConstructor] //指定該構造函數須要被注入 14 public WinPhone4(IPower iPower) 15 { 16 Console.WriteLine("{0}被構造", this.GetType().Name); 17 } 18 19 public void Call() 20 { 21 Console.WriteLine("{0}打電話", this.GetType().Name); ; 22 } 23 24 }
1 /// <summary> 2 /// 測試構造函數注入(指定參數依賴的註冊名稱) 3 /// </summary> 4 public class WinPhone5 : IPhone 5 { 6 public IMicrophone iMicrophone { get; set; } 7 public IHeadphone iHeadphone { get; set; } 8 public IPower iPower { get; set; } 9 public WinPhone5() 10 { 11 12 } 13 [InjectionConstructor] //指定該構造函數須要被注入且指定注入參數的名稱 14 public WinPhone5([Dependency("fk")]IPower iPower) 15 { 16 Console.WriteLine("{0}被構造", this.GetType().Name); 17 } 18 19 public void Call() 20 { 21 Console.WriteLine("{0}打電話", this.GetType().Name); ; 22 } 23 24 }
/// <summary> /// 測試構造函數注入(指定參數值) /// </summary> public class WinPhone6 : IPhone { public IMicrophone iMicrophone { get; set; } public IHeadphone iHeadphone { get; set; } public IPower iPower { get; set; } public WinPhone6() { } [InjectionConstructor] //指定該構造函數須要被注入且指定注入參數的值 public WinPhone6(IPower iPower,string name) { Console.WriteLine("{0}被構造", this.GetType().Name); } public void Call() { Console.WriteLine("{0}打電話", this.GetType().Name); ; } }
1 //方式三:構造函數注入(又分4中狀況) 2 /* 3 A. 默認方式 4 B. 指定構造函數 5 C. 指定參數依賴的註冊名稱 6 D. 指定參數值 7 */ 8 Console.WriteLine("-----------------------方式三:構造函數注入(又分4中狀況)----------------------------"); 9 //A. 默認注入參數最多的構造函數 10 { 11 Console.WriteLine("-----------------------A. 默認注入參數最多的構造函數----------------------------"); 12 container.RegisterType<IPhone, WinPhone3>(); 13 container.RegisterType<IPower, Power>(); 14 IPhone iphone = container.Resolve<IPhone>(); 15 iphone.Call(); 16 } 17 //B. 指定構造函數 18 { 19 Console.WriteLine("-----------------------B. 指定構造函數----------------------------"); 20 container.RegisterType<IPhone, WinPhone4>(); 21 container.RegisterType<IPower, Power>(); 22 IPhone iphone = container.Resolve<IPhone>(); 23 iphone.Call(); 24 } 25 //C. 指定參數依賴的註冊名稱 26 { 27 Console.WriteLine("-----------------------C. 指定參數依賴的註冊名稱----------------------------"); 28 container.RegisterType<IPhone, WinPhone5>(); 29 container.RegisterType<IPower, Power>("fk"); 30 IPhone iphone = container.Resolve<IPhone>(); 31 iphone.Call(); 32 } 33 //D.指定參數值(瞭解便可) 34 { 35 Console.WriteLine("-----------------------D.指定參數值(瞭解便可)----------------------------"); 36 container.RegisterType<IPhone, WinPhone6>(new InjectionConstructor(new Power() { }, "ypf")); 37 IPhone iphone = container.Resolve<IPhone>(); 38 iphone.Call(); 39 }
小結一下: 以上內容分別介紹了IOC和DI的概念、Unity的使用方式和建立對象的方式、DI的三種注入方式。細心的人或者有經驗的人必定會發現,上述代碼依舊須要添加對Service層和Interface層的引用,那麼用Unity有什麼用?使用了Untiy來建立對象有何好處呢?
咱們本身手寫IOC的方式(反射+工廠+配置文件)的方式已經能夠脫離對Service層的直接引用了,怎麼引入Unity反而又須要添加對Service層的引用呢?這不是徒增煩惱麼?
下面咱們來解惑:上述全部代碼(包括Unity步驟的三步走)都是在代碼層次上,介紹Unity的基本API的使用,方便咱們你們明白Unity有哪些功能,但在實際開發中,Unity三步走中的第二步,註冊類型是經過配置文件來註冊的,建立Unity容器和解析類型是經過代碼來實現的→→這樣就能夠達到 脫離Service層的引用了(只須要把Service層的dll複製到主程序的bin文件下便可)。
有的人還會發現:即便使用了配置文件最多也就是和手寫iOC打了個平手,並無看到Unity特有的好處,咱們下個章節將介紹的Unity的聲明週期,便是Unity特有的一些封裝,就能夠見識到Unity的強大。