关于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容器会返回上次建立的实例,而不会从新建立一个实例,这也是单例的精髓之处,可是具体的实例销毁时间,多是容器销毁的时候,也多是应用程序销毁的时候,具体我也不是很清楚.