關於Ioc的框架有不少,好比astle Windsor、Unity、Spring.NET、StructureMap,咱們這邊使用微軟提供的Unity作示例,你能夠使用Nuget添加Unity,也能夠引用Microsoft.Practices.Unity.dll和Microsoft.Practices.Unity.Configuration.dll,下面咱們就一步一步的學習下Unity依賴注入的詳細使用。若是不明白什麼是控制反轉和依賴注入,請參考控制反轉和依賴注入模式html
下面經過一個示例來說解Unity不一樣的依賴注入,如今有一家公司,這家公司有不少的員工,這些員工分別來自不一樣的省份,有的是浙江人,有的是四川人,也有的是湖南人等等,由於公司上了必定的規模,因此爲了解決員工的吃飯問題,因此公司決定built一個食堂,可是不一樣地方的員工的口味不一樣,因此食堂必須具有烹飪不一樣菜系的功能,ok,接下來就圍繞這這個例子來說解Unity的依賴注入。app
一、構造器注入框架
IOC容器會智能的選擇和調用合適的構造函數,以建立依賴的對象,若是被選擇的構造函數具備相應的參數,IOC容器在調用構造函數以前會解析註冊的依賴關係並自行得到相應的參數。函數
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Practices.Unity; namespace DependencyInto { class Program { static void Main(string[] args) { UnityContainer contanier = new UnityContainer(); //向IOC容器中註冊浙江系菜和四川系菜 contanier.RegisterType<IMess, SiChuanFood>(); contanier.RegisterType<IMess, ZhengJiangFood>(); //IOC容器會智能的選擇和調用合適的構造函數,以建立依賴的對象,若是被選擇的構造函數具備相應的參數,IOC容器在調用構造函數以前會解析註冊的依賴關係並自行得到相應的參數。 //獲取可行的參數後,將參數注入到對應的類中 IEmployee employee=contanier.Resolve<SiChuanEmployee>(); IEmployee employees = contanier.Resolve<ZheJiangEmployee>(); employee.EatFood(); employees.EatFood(); Console.ReadLine(); } } /// <summary> /// 員工接口,裏面包含員工最基本的權利 /// </summary> internal interface IEmployee { void EatFood(); } /// <summary> /// 食堂接口,裏面包含食堂最基本的用途 /// </summary> internal interface IMess { string GetFood(); } /// <summary> /// 浙江系菜 /// </summary> internal class ZhengJiangFood : IMess { public string GetFood() { return "浙江菜"; } } /// <summary> /// 四川系菜 /// </summary> internal class SiChuanFood : IMess { public string GetFood() { return "四川菜"; } } /// <summary> /// 四川員工 /// </summary> internal class SiChuanEmployee : IEmployee { private IMess _mess; /// <summary> /// 經過構造函數注入食堂接口實例 /// </summary> /// <param name="mess">食堂接口實例</param> public SiChuanEmployee(IMess mess) { this._mess = mess; } public void EatFood() { Console.WriteLine("四川人吃" + _mess.GetFood()); } } /// <summary> /// 浙江員工 /// </summary> internal class ZheJiangEmployee : IEmployee { private IMess _mess; /// <summary> /// 經過構造函數注入食堂接口實例 /// </summary> /// <param name="mess">食堂接口實例</param> public ZheJiangEmployee(IMess mess) { this._mess = mess; } public void EatFood() { Console.WriteLine("浙江人吃"+_mess.GetFood()); } } }
UnityContainer的實例方法:RegisterType 向容器中註冊須要經過容器生成的對象post
UnityContainer的實例方法:Resolve 設置生成的對象的注入目標(就是設置生成的對象須要注入哪一個目標)學習
二、屬性注入-經過Dependency特性ui
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Practices.Unity; namespace DependencyInto { class Program { static void Main(string[] args) { UnityContainer contanier = new UnityContainer(); //向IOC容器中註冊浙江系菜和四川系菜 contanier.RegisterType<IMess, SiChuanFood>(); contanier.RegisterType<IMess, ZheJiangFood>(); //IOC容器會智能的選擇和調用合適的構造函數,以建立依賴的對象,若是被選擇的構造函數具備相應的參數,IOC容器在調用構造函數以前會解析註冊的依賴關係並自行得到相應的參數。 //獲取可行的參數後,將參數注入到對應的類中 IEmployee employee=contanier.Resolve<SiChuanEmployee>(); IEmployee employees = contanier.Resolve<ZheJiangEmployee>(); employee.EatFood(); employees.EatFood(); Console.ReadLine(); } } /// <summary> /// 員工接口,裏面包含員工最基本的權利 /// </summary> internal interface IEmployee { void EatFood(); } /// <summary> /// 食堂接口,裏面包含食堂最基本的用途 /// </summary> internal interface IMess { string GetFood(); } /// <summary> /// 浙江系菜 /// </summary> internal class ZheJiangFood : IMess { public string GetFood() { return "浙江菜"; } } /// <summary> /// 四川系菜 /// </summary> internal class SiChuanFood : IMess { public string GetFood() { return "四川菜"; } } /// <summary> /// 四川員工 /// </summary> internal class SiChuanEmployee : IEmployee { #region 屬性注入依賴 private IMess _mess; [Dependency] public IMess Mess { get { return this._mess; } set { this._mess = value; } } #endregion public void EatFood() { Console.WriteLine("四川人吃" + Mess.GetFood()); } } /// <summary> /// 浙江員工 /// </summary> internal class ZheJiangEmployee : IEmployee { #region 屬性注入依賴 private IMess _mess; [Dependency] public IMess Mess { get { return this._mess; } set { this._mess = value; } } #endregion public void EatFood() { Console.WriteLine("浙江人吃"+_mess.GetFood()); } } }
ok,輸出結果同樣,經過Dependency特性聲明須要外部注入依賴的屬性,注:該特性this
三、方法注入-經過InjectionMethod特性url
方法注入和屬性方式使用同樣,方法注入只須要在方法前加[InjectionMethod]標記就好了從方法注入的定義上看,只是模糊的說對某個方法注入,可是方法注入無非三種:spa
a、方法參數注入
b、方法返回值注入
c、方法中的引用注入
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Practices.Unity; namespace DependencyInto { class Program { static void Main(string[] args) { UnityContainer contanier = new UnityContainer(); //向IOC容器中註冊浙江系菜和四川系菜 contanier.RegisterType<IMess, SiChuanFood>(); contanier.RegisterType<IMess, ZheJiangFood>(); //IOC容器會智能的選擇和調用合適的構造函數,以建立依賴的對象,若是被選擇的構造函數具備相應的參數,IOC容器在調用構造函數以前會解析註冊的依賴關係並自行得到相應的參數。 //獲取可行的參數後,將參數注入到對應的類中 IEmployee employee = contanier.Resolve<SiChuanEmployee>(); ZheJiangEmployee employees = contanier.Resolve<ZheJiangEmployee>(); employee.EatFood(); employees.EatFood(); Console.WriteLine("people.tool == null(引用) ? {0}", employees._mess1== null ? "Yes" : "No"); Console.WriteLine("people.tool2 == null(參數) ? {0}", employees._mess2 == null ? "Yes" : "No"); Console.WriteLine("people.tool3 == null(返回值) ? {0}", employees._mess3 == null ? "Yes" : "No"); Console.ReadLine(); } } /// <summary> /// 員工接口,裏面包含員工最基本的權利 /// </summary> internal interface IEmployee { void EatFood(); } /// <summary> /// 食堂接口,裏面包含食堂最基本的用途 /// </summary> internal interface IMess { string GetFood(); } /// <summary> /// 浙江系菜 /// </summary> internal class ZheJiangFood : IMess { public string GetFood() { return "浙江菜"; } } /// <summary> /// 四川系菜 /// </summary> internal class SiChuanFood : IMess { public string GetFood() { return "四川菜"; } } /// <summary> /// 四川員工 /// </summary> internal class SiChuanEmployee : IEmployee { #region 方法注入 public IMess _mess1;//我是對象引用 public IMess _mess2;//我是參數 public IMess _mess3;//我是返回值 /// <summary> /// 經過方法裏面的引用注入 /// </summary> [InjectionMethod] public void MethodInto1() { if (object.ReferenceEquals(_mess1, null)) { } } /// <summary> /// 經過方法參數注入 /// </summary> /// <param name="mess"></param> [InjectionMethod] public void MethodInto2(IMess mess) { this._mess2 = mess; } /// <summary> /// 經過方法返回值注入 /// </summary> /// <param name="mess"></param> [InjectionMethod] public IMess MethodInto3() { return _mess3; } #endregion public void EatFood() { Console.WriteLine("四川人吃" + _mess2.GetFood()); } } /// <summary> /// 浙江員工 /// </summary> internal class ZheJiangEmployee : IEmployee { #region 方法注入 public IMess _mess1;//我是對象引用 public IMess _mess2;//我是參數 public IMess _mess3;//我是返回值 /// <summary> /// 經過方法裏面的引用注入 /// </summary> [InjectionMethod] public void MethodInto1() { if (object.ReferenceEquals(_mess1, null)) { } } /// <summary> /// 經過方法參數注入 /// </summary> /// <param name="mess"></param> [InjectionMethod] public void MethodInto2(IMess mess) { this._mess2 = mess; } /// <summary> /// 經過方法返回值注入 /// </summary> /// <param name="mess"></param> [InjectionMethod] public IMess MethodInto3() { return _mess3; } #endregion public void EatFood() { Console.WriteLine("浙江人吃" + _mess2.GetFood()); } } }
四、配置文件配置IOC
到目前位置三種依賴注入的三種方式,都已近介紹了,可是除了構造器注入當咱們使用屬性注入和方法注入的時候,並經過RegisterType,會產生代碼產生耦合,當咱們添加一個方法或者一個屬性或者添加一個方法,都須要去修改代碼,這中設計顯然是不太合理的,因此咱們要作的是,不去修改代碼而是經過修改配置文件的方式,具體代碼以下:
app.config
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration" /> </configSections> <unity> <containers> <container name="defaultContainer"> <register type="DependencyInto.IMess, DependencyInto" mapTo="DependencyInto.ZheJiangFood, DependencyInto"/> <register type="DependencyInto.IEmployee,DependencyInto" mapTo="DependencyInto.ZheJiangEmployee,DependencyInto"/> </container> </containers> </unity> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> </configuration>
指定自定義節點名稱,和處理自定義節點的通常處理程序
unity配置節點
using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Practices.Unity; using Microsoft.Practices.Unity.Configuration; namespace DependencyInto { class Program { static void Main(string[] args) { UnityContainer container = new UnityContainer(); //UnityConfigurationSection.SectionName="untiy" //經過GetSection()得到unity自定義節點,而且根據該節點下面的內容生成UnityConfigurationSection實例 UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName); configuration.Configure(container, "defaultContainer");//設置容器的名稱,並對其進行配置 IEmployee people = container.Resolve<IEmployee>(); people.EatFood(); Console.ReadLine(); } } /// <summary> /// 員工接口,裏面包含員工最基本的權利 /// </summary> internal interface IEmployee { void EatFood(); } /// <summary> /// 食堂接口,裏面包含食堂最基本的用途 /// </summary> internal interface IMess { string GetFood(); } /// <summary> /// 浙江系菜 /// </summary> internal class ZheJiangFood : IMess { public string GetFood() { return "浙江菜"; } } /// <summary> /// 四川系菜 /// </summary> internal class SiChuanFood : IMess { public string GetFood() { return "四川菜"; } } /// <summary> /// 四川員工 /// </summary> internal class SiChuanEmployee : IEmployee { #region 方法注入 public IMess _mess1;//我是對象引用 public IMess _mess2;//我是參數 public IMess _mess3;//我是返回值 /// <summary> /// 經過方法裏面的引用注入 /// </summary> [InjectionMethod] public void MethodInto1() { if (object.ReferenceEquals(_mess1, null)) { } } /// <summary> /// 經過方法參數注入 /// </summary> /// <param name="mess"></param> [InjectionMethod] public void MethodInto2(IMess mess) { this._mess2 = mess; } /// <summary> /// 經過方法返回值注入 /// </summary> /// <param name="mess"></param> [InjectionMethod] public IMess MethodInto3() { return _mess3; } #endregion public void EatFood() { Console.WriteLine("四川人吃" + _mess2.GetFood()); } } /// <summary> /// 浙江員工 /// </summary> internal class ZheJiangEmployee : IEmployee { #region 方法注入 public IMess _mess1;//我是對象引用 public IMess _mess2;//我是參數 public IMess _mess3;//我是返回值 /// <summary> /// 經過方法裏面的引用注入 /// </summary> [InjectionMethod] public void MethodInto1() { if (object.ReferenceEquals(_mess1, null)) { } } /// <summary> /// 經過方法參數注入 /// </summary> /// <param name="mess"></param> [InjectionMethod] public void MethodInto2(IMess mess) { this._mess2 = mess; } /// <summary> /// 經過方法返回值注入 /// </summary> /// <param name="mess"></param> [InjectionMethod] public IMess MethodInto3() { return _mess3; } #endregion public void EatFood() { Console.WriteLine("浙江人吃" + _mess2.GetFood()); } } }
輸出:
五、ContainerControlledLifetimeManager單例
若是不清楚單例模式,請參考Sington(單例模式),Unity提供了單例模式,並將單例實例的生命週期叫給了對應的容器管理,代碼以下:
UnityContainer container = new UnityContainer(); container.RegisterType<IMess, ZheJiangFood>("aa"); IMess ee = container.Resolve<ZheJiangFood>("aa"); IMess ee1 = container.Resolve<ZheJiangFood>("aa"); Console.WriteLine("same instance?ansmer is {0}", object.ReferenceEquals(ee, ee1)); Console.ReadLine();
修改第二行代碼以下:
container.RegisterType<IMess, ZheJiangFood>("aa",new ContainerControlledLifetimeManager());
上面演示了將IMess註冊爲ZheJiangFood,並聲明爲單例,ContainerControlledLifetimeManager字面意思上就是Ioc容器管理聲明週期,咱們也能夠不使用類型映射,將某個類註冊爲單例:
UnityContainer container = new UnityContainer(); ZheJiangFood food = new ZheJiangFood(); container.RegisterInstance<IMess>("aa",food,new ContainerControlledLifetimeManager()); IMess ee = container.Resolve<IMess>("aa"); IMess ee1 = container.Resolve<IMess>("aa"); Console.WriteLine("same instance?ansmer is {0}", object.ReferenceEquals(ee, ee1)); Console.ReadLine();
當咱們聲明一個類型爲ContainerControlledLifetimeManager,說明該類型就是單例,因此當咱們在程序中中獲取該類型的實例時,IOC容器會返回上次建立的實例,而不會從新建立一個實例,這也是單例的精髓之處,可是具體的實例銷燬時間,多是容器銷燬的時候,也多是應用程序銷燬的時候,具體我也不是很清楚.