關于控制反轉(Inversion of Control)和依賴注入(Dependency Injection)大家網上可以找下相關概念,在《小菜學習設計模式(五)—控制反轉(Ioc)》這篇文章中本人也有詳細的解釋,這邊再說明下,有很多人把控制反轉和依賴注入混為一談,雖然在某種意義上來看他們是一體的,但好像又有些不同,就比如在上篇文章中所提到的示例。控制反轉(Ioc)可以看成自來水廠,那自來水廠的運行就可以看作依賴注入(DI),Ioc是一個控制容器,DI就是這個容器的運行機制,有點像國家主席和總理的意思。
關于Ioc的框架有很多,比如astle Windsor、Unity、SPRing.NET、StructureMap,我們這邊使用微軟提供的Unity做示例,你可以使用Nuget添加Unity,也可以引用Microsoft.Practices.Unity.dll和Microsoft.Practices.Unity.Configuration.dll,下面我們就一步一步的學習下Unity依賴注入的詳細使用。
內容有點多,請堅持往下看哦!
構造器注入(Constructor Injection):IoC容器會智能地選擇選擇和調用適合的構造函數以創建依賴的對象。如果被選擇的構造函數具有相應的參數,IoC容器在調用構造函數之前解析注冊的依賴關系并自行獲得相應參數對象。
通過上面的定義看以看出,使用構造器注入需要在在構造函數中傳遞一個抽象參數,Ioc會自動解析具象所依賴的抽象并注冊給具象,我們還是用上篇喝水作為示例:
1 /// <summary> 2 /// 人接口 3 /// </summary> 4 public interface ipeople 5 { 6 void DrinkWater(); 7 } 8 /// <summary> 9 /// 村民10 /// </summary>11 public class VillagePeople : IPeople12 {13 IWaterTool _pw;14 public VillagePeople(IWaterTool pw)15 {16 _pw = pw;17 }18 public void DrinkWater()19 {20 Console.WriteLine(_pw.returnWater());21 }22 }23 /// <summary>24 /// 壓水井25 /// </summary>26 public class PressWater : IWaterTool27 {28 public string returnWater()29 {30 return "地下水好甜啊!!!";31 }32 }33 /// <summary>34 /// 獲取水方式接口35 /// </summary>36 public interface IWaterTool37 {38 string returnWater();39 }View Code
代碼很簡單,PressWater依賴于IWaterTool,在VillagePeople構造函數中傳遞一個IWaterTool的抽象,我們看下調用代碼:
1 static void Main(string[] args)2 {3 UnityContainer container = new UnityContainer();//創建容器4 container.RegisterType<Test01.IWaterTool, Test01.PressWater>();//注冊依賴對象5 Test01.IPeople people = container.Resolve<Test01.VillagePeople>();//返回調用者6 people.DrinkWater();//喝水7 }
運行結果:
上面主要用到Unity的RegisterType和Resolve的泛型方法,我們看下RegisterType的方法簽名:
1 // 2 // 摘要: 3 // Register a type mapping with the container. 4 // 5 // 參數: 6 // container: 7 // Container to configure. 8 // 9 // injectionMembers:10 // Injection configuration objects.11 //12 // 類型參數:13 // TFrom:14 // System.Type that will be requested.15 //16 // TTo:17 // System.Type that will actually be returned.18 //19 // 返回結果:20 // The Microsoft.Practices.Unity.UnityContainer object that this method was21 // called on (this in C#, Me in Visual Basic).22 //23 // 備注:24 // This method is used to tell the container that when asked for type TFrom,25 // actually return an instance of type TTo. This is very useful for getting26 // instances of interfaces.27 // This overload registers a default mapping and transient lifetime.28 public static IUnityContainer RegisterType<TFrom, TTo>(this IUnityContainer container, params InjectionMember[] injectionMembers) where TTo : TFrom;View Code
我們可以看到RegisterType的第一個參數是this IUnityContainer container,我們上面調用的時候并沒有傳遞一個IUnityContainer 類型的參數,為什么這里會有一個this關鍵字,做什么用?其實這就是擴展方法。這個擴展方法在靜態類中聲明,定義一個靜態方法(UnityContainerExtensions類和RegisterType都是靜態的),其中第一個參數定義可它的擴展類型。RegisterType方法擴展了UnityContainerExtensions類,因為它的第一個參數定義了IUnityContainer(UnityContainerExtensions的抽象接口)類型,為了區分擴展方法和一般的靜態方法,擴展方法還需要給第一個參數使用this關鍵字。
還有就是RegisterType的泛型約束 where TTo : TFrom;TTo必須是TFrom的派生類,就是說TTo依賴于TFrom。
我們再來看下Resolve泛型方法的簽名:
1 // 2 // 摘要: 3 // Resolve an instance of the default requested type from the container. 4 // 5 // 參數: 6 // container: 7 // Container to resolve from. 8 // 9 // overrides:10 // Any overrides for the resolve call.11 //12 // 類型參數:13 // T:14 // System.Type of object to get from the container.15 //16 // 返回結果:17 // The retrieved object.18 public static T Resolve<T>(this IUnityContainer container, params ResolverOverride[] overrides);View Code
“Resolve an instance of the default requested type from the container”,這句話可以翻譯為:解決從容器的默認請求的類型的實例,就是獲取調用者的對象。
關于RegisterType和Resolve我們可以用自來水廠的例子來說明,請看下面:
屬性注入(Property Injection):如果需要使用到被依賴對象的某個屬性,在被依賴對象被創建之后,IoC容器會自動初始化該屬性。
屬性注入只需要在屬性字段前面加[Dependency]標記就行了,如下:
1 /// <summary> 2 /// 村民 3 /// </summary> 4 public class VillagePeople : IPeople 5 { 6 [Dependency] 7 public IWaterTool _pw { get; set; } 8 public void DrinkWater() 9 {10 Console.WriteLine(_pw.returnWater());11 }12 }
調用方式和構造器注入一樣,通過RegisterType<Test02.IWaterTool, Test02.PressWater>();注入就可以了,除了使用RegisterType方法注冊,我們還可以在配置文件中注冊,[Dependency]和RegisterType方式其實都會產生耦合度,我們要添加一個屬性或是修改一中注冊都會去修改代碼,我們要做的就是代碼不去修改,只要修改配置文件了,這個在下面有講解,這邊就不多說,我們先看下使用UnityConfigurationSection的Configure方法加載配置文件注冊:
1 <unity>2 <containers>3 <container name="defaultContainer">4 <register type="UnityContainerDemo.IWaterTool,UnityContainerDemo" mapTo="UnityContainerDemo.PressWater,UnityContainerDemo"/>5 <register type="UnityContainerDemo.IPeople,UnityContainerDemo" mapTo="UnityContainerDemo.VillagePeople02,UnityContainerDemo"/>6 </container>7 </containers>8 </unity>
調用代碼:
1 public static void FuTest02()2 {3 UnityContainer container = new UnityContainer();//創建容器4 UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);5 configura
新聞熱點
疑難解答