Ioc-Unity整理

来源:互联网 发布:免费顶级域名注册 编辑:程序博客网 时间:2024/06/01 07:26

参考 http://blog.csdn.net/wanzhuan2010/article/details/7763280

http://www.it165.net/pro/html/201407/17685.html


基础概念

      IOC(Inversion of Control )——控制反转

即依赖对象不在被依赖模块的类中直接通过new来获取

DI(Dependency Injection)——依赖注入

DI是IoC的一种实现方式,就是将依赖对象的创建和绑定转移到被依赖对象类的外面来实现 

依赖注入分为:构造函数注入、属性注入和接口注入

Unit是微软patterns& practices组用C#实现的轻量级、可扩展的依赖注入容器

对于小型项目:用代码的方式实现即可

对于中大型项目:使用配置文件比较好


下面开始Unity之旅


用编程方式实现注入


新建一个控制台应用程序,引用Microsoft.Practices.Unity.dll文件; 

新建一个鸟类的接口,定义一个鸟叫的方法;

[csharp] view plaincopy
  1. /// <summary>  
  2. /// 鸟类接口  
  3. /// </summary>  
  4. public interface IBird  
  5. {  
  6.     /// <summary>  
  7.     /// 讲话  
  8.     /// </summary>  
  9.     void Say( );  
  10. }  

对这个接口进行实现:

[csharp] view plaincopy
  1. /// <summary>  
  2. /// 燕子  
  3. /// </summary>  
  4. public class Swallow : IBird  
  5. {  
  6.     public void Say( )  
  7.     {  
  8.         Console.WriteLine("燕子在叫...");  
  9.     }  
  10. }  


在Mian方法中通过Unity实现IOC反转控制;

[csharp] view plaincopy
  1. static void Main( string[] args )  
  2.        {  
  3.            //实例化一个控制器  
  4.            IUnityContainer unityContainer = new UnityContainer();  
  5.            //实现注入  
  6.            unityContainer.RegisterType<IBird, Swallow>();  
  7.            IBird bird = unityContainer.Resolve<IBird>();  
  8.   
  9.            bird.Say();  
  10.              
  11.            Console.Read();  

运行结果:



这个小实例已经实现了简单的IOC控制反转.


当一个接口有多个实现,而且没有用别名区分时,就会选择最后一个注入的实现;

[csharp] view plaincop
  1. public class Sparrow : IBird  
  2. {  
  3.     public void Say()  
  4.     {  
  5.         Console.WriteLine("麻雀在叫....");  
  6.     }  
  7. }  

Main方法代码:

[csharp] view plaincopy
  1. //实例化一个控制器  
  2. IUnityContainer unityContainer = new UnityContainer();  
  3. //实现注入  
  4. unityContainer.RegisterType<IBird, Swallow>();  
  5. unityContainer.RegisterType<IBird, Sparrow>();  
  6.   
  7. IBird bird = unityContainer.Resolve<IBird>();  
  8.   
  9. bird.Say();  
  10.   
  11. Console.Read();  

运行一下,结果:


下边给每个注入都加上别名:

[csharp] view plaincopy
  1. //实例化一个控制器  
  2. IUnityContainer unityContainer = new UnityContainer();  
  3. //实现注入,用别名区分实现  
  4. unityContainer.RegisterType<IBird, Swallow>("Swallow");  
  5. unityContainer.RegisterType<IBird, Sparrow>("Sparrow");  
  6.   
  7. IBird swallow = unityContainer.Resolve<IBird>("Swallow");  
  8. IBird sparrow = unityContainer.Resolve<IBird>("Sparrow");  
  9.   
  10. swallow.Say();  
  11. sparrow.Say();  
  12.   
  13. Console.Read();  
运行结果:



(二) Unity的构造函数注入


新建 一个IBirdHome 接口,并对接口进行实现:

[csharp] view plaincopy
  1. /// <summary>  
  2. /// 小鸟的家  
  3. /// </summary>  
  4. public interface IBirdHome  
  5. {  
  6.     IBird Swallow { getset; }  
  7. }  
  8.   
  9. /// <summary>  
  10. /// 小鸟的家  
  11. /// </summary>  
  12. public class BirdHome : IBirdHome  
  13. {  
  14.     public IBird Swallow { getset; }  
  15.   
  16.     public BirdHome(IBird bird)  
  17.     {  
  18.         this.Swallow = bird;  
  19.     }  
  20. }  


main方法如下:

[csharp] view plaincopy
  1. //实例化一个控制器  
  2. IUnityContainer unityContainer = new UnityContainer();  
  3. //实现注入  
  4. unityContainer.RegisterType<IBird, Swallow>();  
  5. unityContainer.RegisterType<IBirdHome, BirdHome>();  
  6.   
  7. IBirdHome birdHome = unityContainer.Resolve<IBirdHome>();  
  8. birdHome.Swallow.Say();  
  9.   
  10. Console.Read();  


运行结果:



我们只是通过Unity得到了一个IBirdHome实现,但并没有对 IBird Swallow { get; set; } 进行实例化,但结果已经运行出来,并没有报错.

原来这些工作Unity已帮我们做了,我们可以偷个懒了.嘿嘿.......


(三)属性注入


把BirdHome类的中构造函数去掉,在属性上加上[Dependency]特性

[csharp] view plaincopy
  1. /// <summary>  
  2. /// 小鸟的家  
  3. /// </summary>  
  4. public class BirdHome : IBirdHome  
  5. {  
  6.     /// <summary>  
  7.     /// 属性注入  
  8.     /// </summary>  
  9.     [Dependency]  
  10.     public IBird Swallow { getset; }  
  11. }  

(四) 方法注入

方法注入与构造函数注入相似,但不用写到构造函数里边,而是在初使化方法上加上[InjectionMethod]特性

[csharp] view plaincopy
  1.         /// <summary>  
  2.         /// 初始化器注入  
  3.         /// </summary>  
  4.         /// <param name="bird"></param>  
  5.        [InjectionMethod]  
  6.         public void Initialize(IBird bird)  
  7.         {  
  8.             this.Swallow = bird;  
  9.         }  


运行结果依然是:



配置文件方式

<?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>    <!--定义类型别名-->    <aliases>      <add alias='IClass' type='ConsoleApplication1.UnityDemo.IClass,ConsoleApplication1' />      <add alias='CbClass' type='ConsoleApplication1.UnityDemo.CbClass,ConsoleApplication1' />      <add alias='EcClass' type='ConsoleApplication1.UnityDemo.EcClass,ConsoleApplication1' />    </aliases>    <!--容器-->    <container name='FirstClass'>      <!--映射关系-->      <register type='IClass'  mapTo='CbClass'></register>      <register type='IClass'  mapTo='EcClass' name='ec'></register>    </container>  </unity></configuration>

注入代码如下:
public static void ContainerConfiguration(){      IUnityContainer container = new UnityContainer();//获取指定名称的配置节     UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection('unity');      container.LoadConfiguration(section, 'FirstClass');//获取特定配置节下已命名的配置节<container name='FirstClass'>下的配置信息  IClass classInfo = container.Resolve<IClass>('ec');   classInfo. ShowInfo();}
也可以指定配置文件比如Unity.config
public static void ContainerConfigurationFromFile(string configFile){    //根据文件名获取指定config文件    var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = configFile };    //从config文件中读取配置信息    Configuration configuration =    ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);    var unitySection = (UnityConfigurationSection)configuration.GetSection('unity');    var container = new UnityContainer().LoadConfiguration(unitySection, 'FirstClass');    IClass classInfo = container.Resolve<IClass>('ec');    classInfo.ShowInfo();}

使用Unity为已存在的对象注册关系


在日常开发的过程中我们有时候会自己创建好一个对象,但是你又想对这个已经创建好的对象的生命周期进行管理

public static void RegisterInstance(){    IClass myClass = new MyClass();    IClass yourClass = new YourClass();    //为myClass实例注册默认实例    container.RegisterInstance<IClass>(myClass);    //为yourClass实例注册命名实例,同RegisterType    container.RegisterInstance<IClass>('yourInstance', yourClass);    container.Resolve<IClass>().ShowInfo();    container.Resolve<IClass>('yourInstance').ShowInfo();}

Unity中生命周期管理

简单介绍一下Unity中内置的两个常用生命周期管理器,其他的生命周期管理器如果需要可以自己上网查看其详细说明
(1)TransientLifetimeManager,瞬态生命周期,默认情况下,在使用RegisterType进行对象关系注册时如果没有指定生命周期管理器则默认使用这个生命周期管理器,这个生命周期管理器就如同其名字一样,当使用这种管理器的时候,每次通过ResolveResolveAll调用对象的时候都会重新创建一个新的对象
public static void TransientLifetimeManagerCode(){    //以下2种注册效果是一样的    container.RegisterType<IClass, MyClass>();    container.RegisterType<IClass, MyClass>(new TransientLifetimeManager());    Console.WriteLine('-------TransientLifetimeManager Begin------');    Console.WriteLine('第一次调用RegisterType注册的对象HashCode:' +    container.Resolve<IClass>().GetHashCode());    Console.WriteLine('第二次调用RegisterType注册的对象HashCode:' +    container.Resolve<IClass>().GetHashCode());    Console.WriteLine('-------TransientLifetimeManager End------');}
如果是使用配置的方式,则需要在配置文件中注册关系的时候在<register>配置节下新增<lifetime>既可(如果不新增则默认使用TransientLifetimeManager),如果想使用其他的生命周期管理器,则更改此配置节即可!

<register type='IClass' mapTo='MyClass'>  <lifetime type='transient' /></register>

(2)ContainerControlledLifetimeManager,容器控制生命周期管理,这个生命周期管理器是RegisterInstance默认使用的生命周期管理器,也就是单件实例,UnityContainer会维护一个对象实例的强引用,每次调用的时候都会返回同一对象,示例代码如下:

public static void ContainerControlledLifetimeManagerCode(){    IClass myClass = new MyClass();    //以下2种注册效果是一样的    container.RegisterInstance<IClass>('ccl', myClass);    container.RegisterInstance<IClass>('ccl', myClass, new ContainerControlledLifetimeManager());    container.RegisterType<IClass, MyClass>(new ContainerControlledLifetimeManager());    Console.WriteLine('-------ContainerControlledLifetimeManager Begin------');    Console.WriteLine('第一次调用RegisterType注册的对象HashCode:' + container.Resolve<IClass>().GetHashCode());    Console.WriteLine('第二次调用RegisterType注册的对象HashCode:' + container.Resolve<IClass>().GetHashCode());    Console.WriteLine('第一次调用RegisterInstance注册的对象HashCode:' + container.Resolve<IClass>('ccl').GetHashCode());    Console.WriteLine('第二次调用RegisterInstance注册的对象HashCode:' + container.Resolve<IClass>('ccl').GetHashCode());    Console.WriteLine('-------ContainerControlledLifetimeManager End------');}

运行之后都会发现实例的哈希码是一样的,说明是单实例的

如果用此生命周期管理器,则要在配置文件中新增的节点如下:

<register type='IClass' mapTo='MyClass' name='ccl'>   <lifetime type='singleton' /></register>
(3)HierarchicalLifetimeManager,分层生命周期管理器,这个管理器类似于ContainerControlledLifetimeManager,也是由UnityContainer来管理,也就是单件实例。不过与ContainerControlledLifetimeManager不同的是,这个生命周期管理器是分层的,因为Unity的容器时可以嵌套的,所以这个生命周期管理器就是针对这种情况,当使用了这种生命周期管理器,父容器和子容器所维护的对象的生命周期是由各自的容器来管理,代码如下(RegisterInstance情况也类似,这边就不展示了):
public static void HierarchicalLifetimeManagerCode(){    container.RegisterType<IClass, MyClass>(new HierarchicalLifetimeManager());    //创建子容器    var childContainer = container.CreateChildContainer();    childContainer.RegisterType<IClass, MyClass>(new HierarchicalLifetimeManager());     Console.WriteLine("-------ContainerControlledLifetimeManager Begin------");    Console.WriteLine("第一次调用父容器注册的对象HashCode:" +         container.Resolve<IClass>().GetHashCode());    Console.WriteLine("第二次调用父容器注册的对象HashCode:" +         container.Resolve<IClass>().GetHashCode());    Console.WriteLine("第一次调用子容器注册的对象HashCode:" +         childContainer.Resolve<IClass>().GetHashCode());    Console.WriteLine("第二次调用子容器注册的对象HashCode:" +         childContainer.Resolve<IClass>().GetHashCode());    Console.WriteLine("-------ContainerControlledLifetimeManager End------");}

这边需要提一下的就是,Unity这种分级容器的好处就在于我们可以对于有不同生命周期的对象放在不同的容器中,如果一个子容器被释放,不会影响到其它子容器中的对象,但是如果根节点处父容器释放后,所有的子容器都将被释放。
(4)PerResolveLifetimeManager,这个生命周期是为了解决循环引用而重复引用的生命周期,先看一下微软官方给出的实例:

public interface IPresenter{ } public class MockPresenter : IPresenter{    public IView View { get; set; }     public MockPresenter(IView view)    {        View = view;    }} public interface IView{    IPresenter Presenter { get; set; }} public class View : IView{    [Dependency]    public IPresenter Presenter { get; set; }}

从这个例子中可以看出,有2个接口IPresenter和IView,还有2个类MockPresenter和View分别实现这2个接口,同时这2个类中都包含了对另外一个类的对象属性,这个就是一个循环引用,而对应的这个生命周期管理就是针对这种情况而新增的,其类似于TransientLifetimeManager,但是其不同在于,如果应用了这种生命周期管理器,则在第一调用的时候会创建一个新的对象,而再次通过循环引用访问到的时候就会返回先前创建的对象实例(单件实例),代码如下:

public static void PerResolveLifetimeManagerCode(){    var container = new UnityContainer()    .RegisterType<IPresenter, MockPresenter>()    .RegisterType<IView, View>(new PerResolveLifetimeManager());     var view = container.Resolve<IView>();    var tempPresenter = container.Resolve<IPresenter>();    var realPresenter = (MockPresenter)view.Presenter;     Console.WriteLine("-------PerResolveLifetimeManager Begin------");    Console.WriteLine("使用了PerResolveLifetimeManager的对象 Begin");    Console.WriteLine("通过Resolve方法获取的View对象:" +        view.GetHashCode());    Console.WriteLine("View对象中的Presenter对象所包含的View对象:" +        realPresenter.View.GetHashCode());    Console.WriteLine("使用了PerResolveLifetimeManager的对象 End");    Console.WriteLine("");    Console.WriteLine("未使用PerResolveLifetimeManager的对象 Begin");    Console.WriteLine("View对象中的Presenter对象:" +        realPresenter.GetHashCode());    Console.WriteLine("通过Resolve方法获取的View对象:" +        tempPresenter.GetHashCode());    Console.WriteLine("未使用PerResolveLifetimeManager的对象 End");    Console.WriteLine("-------PerResolveLifetimeManager Begin------");}

从代码中可以看出,在注册对象的时候,仅对IView和View应用了PerResolveLifetimeManager,所以第二次访问View对象会返回同一实例。

具体配置文件如下,有关构造函数注入和属性注入的内容在下一篇文章中进行介绍

<alias alias="IPresenter" type="UnityStudyConsole.IPresenter, UnityStudyConsole" /><alias alias="IView" type="UnityStudyConsole.IView, UnityStudyConsole" /><alias alias="MockPresenter" type="UnityStudyConsole.MockPresenter, UnityStudyConsole" /><alias alias="View" type="UnityStudyConsole.View, UnityStudyConsole" /><container name="Second">  <register type="IPresenter" mapTo="MockPresenter">    <constructor>      <param name ="view" type="IView">      </param>    </constructor>  </register>  <register type="IView" mapTo="View" >    <lifetime type="perresolve"/>    <property name="Presenter" dependencyType="IPresenter"></property>  </register></container>

(5)PerThreadLifetimeManager,每线程生命周期管理器,就是保证每个线程返回同一实例,具体代码如下:
public static void PerThreadLifetimeManagerCode(){    container.RegisterType<IClass, MyClass>(new PerThreadLifetimeManager());    var thread = new Thread(new ParameterizedThreadStart(Thread1));    Console.WriteLine("-------PerResolveLifetimeManager Begin------");    Console.WriteLine("默认线程 Begin");    Console.WriteLine("第一调用:" +        container.Resolve<IClass>().GetHashCode());    Console.WriteLine("第二调用:" +        container.Resolve<IClass>().GetHashCode());    Console.WriteLine("默认线程 End");    thread.Start(container);} public static void Thread1(object obj){    var tmpContainer = obj as UnityContainer;    Console.WriteLine("新建线程 Begin");    Console.WriteLine("第一调用:" +         tmpContainer.Resolve<IClass>().GetHashCode());    Console.WriteLine("第二调用:" +         tmpContainer.Resolve<IClass>().GetHashCode());    Console.WriteLine("新建线程 End");

Console.WriteLine("-------PerResolveLifetimeManager End------"); }

有关配置相关的代码与前面的生命周期管理器差不多,这边就不贴代码了,请看示例代码。

具体效果图如下:

pic76

(6)ExternallyControlledLifetimeManager,外部控制生命周期管理器,这个生命周期管理允许你使用RegisterType和RegisterInstance来注册对象之间的关系,但是其只会对对象保留一个弱引用,其生命周期交由外部控制,也就是意味着你可以将这个对象缓存或者销毁而不用在意UnityContainer,而当其他地方没有强引用这个对象时,其会被GC给销毁掉。

在默认情况下,使用这个生命周期管理器,每次调用Resolve都会返回同一对象(单件实例),如果被GC回收后再次调用Resolve方法将会重新创建新的对象,示例代码如下

public static void ExternallyControlledLifetimeManagerCode(){    container.RegisterType<IClass, MyClass>(new ExternallyControlledLifetimeManager());    var myClass1 = container.Resolve<IClass>();    var myClass2 = container.Resolve<IClass>();    Console.WriteLine("-------ExternallyControlledLifetimeManager Begin------");    Console.WriteLine("第一次调用:" +        myClass1.GetHashCode());    Console.WriteLine("第二次调用:" +        myClass2.GetHashCode());    myClass1 = myClass2 = null;    GC.Collect();    Console.WriteLine("****GC回收过后****");    Console.WriteLine("第一次调用:" +       container.Resolve<IClass>().GetHashCode());    Console.WriteLine("第二次调用:" +       container.Resolve<IClass>().GetHashCode());     Console.WriteLine("-------ExternallyControlledLifetimeManager End------");}

有关配置相关的代码与前面的生命周期管理器差不多,这边就不贴代码了,请看示例代码。

效果图如下:

pic77

 

以上就是本文的全部内容了,主要介绍了使用UnityContainer来注册对象之间的关系、注册已存在的对象之间的关系和Unity内置的生命周期管理器。


0 0
原创粉丝点击