Unity TIP1: 基础
Unity属于微软企业库的一部分,你可以到http://entlib.codeplex.com/去下载企业库并安装。
为了介绍Unity,应该首先明白两个概念(见[EntLib]微软企业库5.0 学习之路——第十步、使用Unity解耦你的系统—PART1——为什么要使用Unity?)。
1、IOC(Inversion of Control )——反转控制,根据其字面意思我们就可以知道就是将控制权反转出去。
在我们以往开发代码的过程中每一层都紧紧地联系在一起,一层依赖一层,如果一层发生了变化那就会导致其他层也发生连锁反应,例如:业务逻辑层(BLL)依赖于数据访问层(DAL),一般都是直接创建数据访问层相应的对象,如:
public class UserManage { UserService serivce = new UserService(); public void Insert(User user) { service.Insert(user); } }
可以看出,以上的代码中,业务逻辑层直接通过实例化的方式来进行对数据访问层的操作,乍看之下没什么问题,但是当项目达到一定的规模,同时业务逻辑发生变更时(如将依赖于其他的数据访问层或者特殊的数据访问层时),这些代码都是一个个隐藏的炸弹,一旦爆炸起来会有让你重写代码的冲动。
IOC的出现正是为了解决这种问题,使用过工厂模式的朋友一定会知道,我们可以通过特定配置或者约定来根据具体的情况来反射加载或创建所需的对象实例,而IOC可以说就是工厂模式的升级版本,IOC接管了这些复杂的层与层、接口与类或者类与类之间的映射关系,我们在编写代码的时候无需考虑我到底需要什么,只要告诉IOC我要什么,IOC会根据我们所配置或约定好的来提供我们所需要的对象。
Unity就是这样一个优秀的、轻量级的IOC容器,我们可以通过代码或者XML配置文件来配置对象与对象之间的关系,在运行时直接调用Unity容器既可获取我们所需的对象。
2、DI(Dependency Injection),依赖注入,从字面上可以看出,其意思就是根据依赖关系进行注入。
依赖注入分为:构造函数注入、属性注入和方法注入。
对于一个类对象来说,主要暴露给其它对象的3个成员就是构造函数、属性和方法,在这3类成员中都可以存在于依赖其他类或接口参数。例如:
public class A { public A(B b) { this.B = b; } public B B { get; set; } public void Test(B b) { Console.WriteLine(b.ToString()); } }
可以看到上面的代码中,在A类中的构造函数、属性以及方法中都有对B类对象的依赖,在一般情况下,如果想实例化A类,则必须传递B对象,但是如果B对象为NULL,那在接下来的代码中如果直接对属性B进行操作则会出现异常,同理,如果在调用方法Test,传递一个为NULL值的B对象,一样会引发异常,而我们的常规做法则是在调用前事先构建好一个B对象,然后传递进去,但是这样做的话就会导致代码重复,这时就体现出了DI的重要性了,我们可以通过IOC来对这些依赖进行创建,这样就无需我们在调用前还要想好先对调用的对象依赖进行创建,仅仅只需要事先配置好映射,大大的方便了我们日常的开发。
总的来说,DI是实现IOC的方式,IOC的思想是解除对象与对象之间的依赖,由IOC来进行控制,而DI则是IOC思想的具体实现。
3:一个最简单的示例
首先,应该引入Microsoft.Practices.Unity。其次,相关代码如下:
public interface IClass { void ShowInfo(); } public class MyClass : IClass { public MyClass() { } public void ShowInfo() { Console.WriteLine("这个是我的班级"); } } class Program { static void Main(string[] args) { //IClass classInfo = new MyClass(); //classInfo.ShowInfo(); IUnityContainer container = new UnityContainer(); container.RegisterType<IClass, MyClass>(); //另一种注册方法,不过没有RegisterType<>()方法来的方便 //container.RegisterType(typeof(IClass), typeof(MyClass)); IClass classInfo = container.Resolve<IClass>(); //另一种通过container获取具体对象的方法 //IClass classInfo = container.Resolve(typeof(IClass)); classInfo.ShowInfo(); } }
4:问题来了
以上是一个最简单的示例。你可能已经理解了Unity的基本实现语法,但是你可能同样会疑问:在主程序中,接口和实现类都被耦合进来了,那我们使用Unity还有什么意思。Unity的强大之处应该体现在解耦。也就是说,主程序或者说主项目应该只和接口耦合。即,我们的程序的结构应该是这样的:
依赖注入的信息应该是可配置的,如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/> </configSections> <unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> <alias alias="IClass" type="IDal.IClass, IDal" /> <alias alias="MyClass" type="Dal.MyClass, Dal" /> <container name="containerOne"> <register type="IClass" name="ConfigClass" mapTo="MyClass" /> </container> </unity> </configuration>
此配置文件当前还是比较简单的,相信各位一看就能明白是什么意思。而主程序我们修改为:
IUnityContainer container = new UnityContainer(); UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); section.Containers["containerOne"].Configure(container); IClass classInfo = container.Resolve<IClass>("ConfigClass"); classInfo.ShowInfo();
最后要注意的几点是:
1:为了要读取配置信息,主程序要引入Microsoft.Practices.Unity.Configuration.dll;
2:由于主项目不会引入DAL项目,所以DAL的输出文件,即DAL.DLL要手动拷贝到运行目录下;