关于控制反转(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
新闻热点
疑难解答