[EntLib]微软企业库5.0 学习之路——第十步、使用Unity解耦你的系统—PART3——依赖注入
继续学习Unity,在前几篇中已经将Unity的使用方法做了一个还算详细的介绍了,主要是如何通过代码及配置文件来注册对象之间的关系、Unity内置所有的生命周期管理使用介绍,及Unity的Register和Resolve的一些高级应用。通过在PART1——为什么要使用Unity?的学习我们知道Unity可以帮我们简化并管理对象之间的关系(也就是前几篇所介绍的),而今天则要介绍Unity的另外一个重要功能——DI(依赖注入)。
本篇文章将主要介绍:
1、构造函数注入。
2、属性注入。
3、方法注入。
4、使用配置完成各种注入。
5、对已经创建对象进行注入。
一、构造函数注入
在有些时候,我们所编写的类中的构造函数都会包含对其他对象的引用,如下代码:
1 2 3 4 5 | public Subject2(MyClass myClass1,MyClass myClass2) { myClass1.Name = "班级1" ; myClass2.Name = "班级2" ; } |
可以看到这个构造函数有2个参数,都依赖于MyClass类,如果一般情况下想要调用,我们总是需要实现构建好2个myclass对象,所以相对来说比较麻烦,而如果使用Unity来调用就会方便许多,如下代码:
1 | container.Resolve<Subject2>(); |
仅仅一行既可,Unity容器会自动帮我们构建好所需的依赖对象实例。
当然这个只是简单的使用,在实际的情况下我们不会这么编写代码,我们不会直接引用对象,而是直接引用接口,这样可以解除代码的耦合性,如下代码:
1 2 3 4 | public Subject2(IClass myClass1) { myClass1.Name = "班级1" ; } |
这样Subject2类的构造函数仅仅依赖于IClass接口,并依赖于具体的实现类,这种情况下,如果想调用的话,需要实现注册好对象之间的关系,如下代码:
1 2 3 4 5 6 7 | public static void ConstructorInjection() { //container.Resolve<Subject2>(); container.RegisterType<IClass, MyClass>(); container.Resolve<Subject2>(); } |
通过Unity容器对象的获取对象时候默认总是获取非命名默认注册对象,但是如果调用Subject2的构造函数参数我想指定具体的对象时怎么办?
这时就需要使用Dependency这个特性类来解决了,在需要特殊指定的依赖关系的参数上加上Dependency特性,并为Dependency指定好参数name(此name参数表示注册对象关系时所指定的名称),代码如下:
1 2 3 4 5 | public Subject2([Dependency( "your" )] IClass classInfo) { classInfo.Name = "班级1" ; } |
调用代码:
1 2 3 | container.RegisterType<IClass, MyClass>() .RegisterType<IClass, YourClass>( "your" ); container.Resolve<Subject2>(); |
可以看到,我对IClass注册了2个对象,而我在Subject2的构造函数参数上使用了Dependency特性指定了ClassInfo参数获取注册名为“your”的对象。
现在还有一个问题,就是当一个类存在多个构造函数的时候,我们如何区分哪个构造函数需要实现注入,哪个不需要?
这时就可以使用InjectionConstructor特性来标识,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 | [InjectionConstructor] public Subject2([Dependency( "your" )] IClass classInfo) { classInfo.Name = "班级1" ; } public Subject2(IClass classInfo1, IClass classInfo2) { classInfo1.Name = "班级1" ; classInfo2.Name = "班级2" ; } |
Unity只会调用标识了InjectionConstructor特性的构造函数,这样就很好的解决了多构造函数的情况下,Unity调用哪个构造函数。
二、属性注入
属性注入和构造函数注入类似,只需在需要注入的属性上增加一个Dependency特性,同样的也可以为Dependency指定一个name参数用来指定注入属性的具体对象,如下代码,在Subject属性上增加了Dependency特性,来表示这个属性需要注入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class MyClass : IClass { public MyClass() { } public void ShowInfo() { Console.WriteLine( "这个是我的班级" ); } [Dependency( "Subject1" )] public ISubject Subject { get ; set ; } public string Name { get ; set ; } public string Description { get ; set ; } } |
具体的调用代码:
1 2 3 4 5 6 7 | public static void PropertyInjection() { container.RegisterType<ISubject, Subject1>( "Subject1" ) .RegisterType<IClass,MyClass>(); var classInfo = container.Resolve<IClass>(); Console.WriteLine(classInfo.Subject.Name); } |
这样ClassInfo的Subject属性自动关联到了Subject1类上(完成了属性注入),访问classInfo.Subject.Name可以得到“科目1”。
三、方法注入
方法注入同样只需在需要注入的方法上增加一个特性——InjectionMethod既可(其使用方法也和构造注入类似),这样Unity会自动帮我们完成注入,方法注入和构造注入一样,同样可以在方法的参数上指定Dependency特性来指定参数所依赖的注册,下面的类代码中包含了构造注入、属性注入及方法注入,这边集合在一起展示相对来说直观一些:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public class MyClass : IClass { public MyClass() { } public MyClass(ISubject subjectInfo) { this .TempSubject1 = subjectInfo; } public void ShowInfo() { Console.WriteLine( "构造注入成功后临时科目1名称:" + this .TempSubject1.Name); Console.WriteLine( "属性注入成功后临时科目名称:" + this .Subject.Name); Console.WriteLine( "方法注入成功后临时科目2名称:" + this .TempSubject2.Name); } [InjectionMethod] public void Init(ISubject subjectInfo) { TempSubject2 = subjectInfo; } [Dependency( "Subject1" )] public ISubject Subject { get ; set ; } public ISubject TempSubject1 { get ; set ; } public ISubject TempSubject2 { get ; set ; } public string Name { get ; set ; } public string Description { get ; set ; } } |
具体的调用代码:
1 2 3 4 5 6 7 8 9 | public static void MethodInjection() { container.RegisterType<ISubject, Subject3>( "Subject1" ) .RegisterType<ISubject, Subject4>(); container.RegisterType<IClass, MyClass>(); var classInfo = container.Resolve<IClass>(); classInfo.ShowInfo(); } |
效果图如下:
4、使用配置完成各种注入
上面所演示的代码都是通过代码来完成对象的注入,下面演示下如何通过配置文件来配置这些注入,具体配置代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <!--依赖注入配置,包括构造注入,方法注入和属性注入--> < alias alias="IClass" type="UnityStudyConsole.IDemo.IClass, UnityStudyConsole" /> < alias alias="MyClass" type="UnityStudyConsole.Demo.MyClass, UnityStudyConsole" /> < alias alias="ISubject" type="UnityStudyConsole.IDemo.ISubject, UnityStudyConsole" /> < alias alias="Subject3" type="UnityStudyConsole.Demo.Subject3, UnityStudyConsole" /> < alias alias="Subject4" type="UnityStudyConsole.Demo.Subject4, UnityStudyConsole" /> < container name="Third"> < register type="IClass" mapTo="MyClass"> < constructor > < param name="subjectInfo" type="ISubject"> < dependency name="subjectInfo" type="Subject4"/> </ param > </ constructor > < method name="Init"> < param name="subjectInfo" type="ISubject"> < dependency name="subjectInfo" type="Subject4"/> </ param > </ method > < property name="Subject"> < dependency name="Subject1" type="Subject3"/> </ property > </ register > </ container > |
代码如下:
1 2 3 4 5 6 7 | public static void DIConfiguration() { //获取特定配置节下已命名的配置节<container name="Third">下的配置信息 container.LoadConfiguration( "Third" ); var classInfo = container.Resolve<IClass>(); classInfo.ShowInfo(); } |
具体的效果是和上面截图中是一样的,只不过这边的依赖注入是通过配置文件来实现的。
5、对已经创建对象进行注入
一般来说如果想实现依赖注入需要通过Unity容器来进行对象注册,然后通过Unity容器来获取对象,但是如果对象已经存在(就是不是通过Unity容器来获取的对象),这时如何来通过Unity来实现对已有的对象进行依赖注入呢?
Unity容器已经为我们提供好了这种情况的解决办法,就是BuildUp方法,看下下面的代码就能明白了:
1 2 3 4 5 6 7 8 9 10 11 | public static void BuildUp() { //事先注册好ISubject和MySubject之间的关系 //并指定一个名称以方便在接口中的属性上应用[Dependency("Subject1")]特性 //具体请见IDemo.IClass container.RegisterType<ISubject, Subject1>( "Subject1" ); IClass classInfo = new MyClass(); IClass classInfo2 = container.BuildUp<IClass>(classInfo); Console.WriteLine(classInfo2.Subject.Name); } #endregion |
在上面的代码中,已经创建好一个对象实例了,这时只需将这个对象作为参数放入BuildUp中,同时还需指定这个对象实例所实现的接口或父类类型,这样Unity就会自动帮我们将这个已存在对象实例中各种注入。
需要注意的是,与上面所说3种依赖注入的不同的是,上面3中的依赖注入需要在具体的类中使用InjectionConstructor、InjectionMethodDependency和特性,而如果对已经存在的对象进行依赖注入,则需要将InjectionConstructor、InjectionMethod和Dependency写在这个对象实例所实现的接口或父类中,否则会报错!
到本文为止,Unity的各种常用功能都已经介绍完毕了,各位可以下载下面的源代码进行查看,同时也可以通过Main方法中的各个方法来查看Unity的各种功能的使用:
示例代码下载:点我下载
(注意:本文示例代码是基于VS2010+Unity2.0,所以请使用VS2010打开,如果没有安装VS2010,请将相关代码复制到相应的VS中运行既可)
微软企业库5.0 学习之路系列文章索引:
第二步、使用VS2010+Data Access模块建立多数据库项目
第三步、为项目加上异常处理(采用自定义扩展方式记录到数据库中)
第四步、使用缓存提高网站的性能(EntLib Caching)
第五步、介绍EntLib.Validation模块信息、验证器的实现层级及内置的各种验证器的使用方法——上篇
第五步、介绍EntLib.Validation模块信息、验证器的实现层级及内置的各种验证器的使用方法——中篇
第五步、介绍EntLib.Validation模块信息、验证器的实现层级及内置的各种验证器的使用方法——下篇
第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—上篇
第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—下篇
第八步、使用Configuration Setting模块等多种方式分类管理企业库配置信息
第九步、使用PolicyInjection模块进行AOP—PART1——基本使用介绍
第九步、使用PolicyInjection模块进行AOP—PART2——自定义Matching Rule
第九步、使用PolicyInjection模块进行AOP—PART3——内置Call Handler介绍
第九步、使用PolicyInjection模块进行AOP—PART4——建立自定义Call Handler实现用户操作日志记录
第十步、使用Unity解耦你的系统—PART1——为什么要使用Unity?
第十步、使用Unity解耦你的系统—PART2——了解Unity的使用方法(1)
第十步、使用Unity解耦你的系统—PART2——了解Unity的使用方法(2)
第十步、使用Unity解耦你的系统—PART2——了解Unity的使用方法(3)
第十步、使用Unity解耦你的系统—PART4——Unity&PIAB
扩展学习:
出处:http://kyo-yo.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?