• 01-Unity深入浅出(一)


    一. 温故而知新

      在开始学习Unity框架之前,有必要温习一下 【依赖倒置原则】和【手写IOC】, 因为我们框架代码的构建都是基于【依赖倒置原则】的,而Unity框架的核心思想就是IOC和DI,所以有必要了解通过【反射+工厂+配置文件】来实现手写IOC的原理。

      废不多说,简单提炼一下依赖原则的核心理念:面向接口编程。上层(UI层)不直接依赖于下层,而是依赖于下层对应的接口,下层去实现该接口。

      经典案例:业务层连接数据库,依赖接口,可以实现不改代码轻松切换数据库的目的。

      手动创建对象的三个阶段:直接new实例化→面向接口编程→将创建对象的交给工厂【手写IOC】。

     1         #region 手动创建对象几个阶段
     2             {
     3                 Console.WriteLine("--------------一.复习依赖倒置原则和手写IOC(简单工厂+反射+配置文件)----------------");
     4                 //1. 直接创建(需要添加对Service层的引用)
     5                 Console.WriteLine("--------------1. 直接创建----------------");
     6                 AndroidPhone androidPhone1 = new AndroidPhone();
     7                 androidPhone1.Call();
     8 
     9                 //2. 利用接口进行改造(需要添加对Service和Interface层的引用)
    10                 Console.WriteLine("--------------2. 利用接口进行改造----------------");
    11                 IPhone androidPhone2 = new AndroidPhone();
    12                 androidPhone2.Call();
    13 
    14                 //3. 手写IOC(只需要添加Interface层的引用,但需要把Service层生成的DLL拷贝到该项目bin下)
    15                 Console.WriteLine("--------------3. 利用接口进行改造----------------");
    16                 IPhone androidPhone3 = ObjectFactory.CreateObject<IPhone>();
    17                 androidPhone3.Call();
    18 
    19                 //总结:第三个阶段基本上已经是我们自己写容器的最高层次
    20                 //下面我们将借助微软的第三方容器Unity来创建
    21 
    22             }
    23             #endregion

    依赖倒置原则详见:http://www.cnblogs.com/yaopengfei/p/7101347.html

    手写IOC详见:http://www.cnblogs.com/yaopengfei/p/6891286.html

    二. Unity深入浅出

     

     (一). Unity介绍

     1. Unity的获取方式

       ①:下载网址:http://unity.codeplex.com/ 

          ②:通过Nuget获取  (目前最新版本为4.0.1)

    2. Unity是什么?

       Unity是一个IOC容器,用它可以来创建对象,代替手写反射,它可以实现依赖注入(DI),它出自于微软。.Net平台下类似的其它IOC框架还有:Spring.Net、AutoFac、Castle等等。

    3. IOC的含义

       IOC是一种目标,IOC的含义为控制反转,即原本上端(UI层)直接创建或使用的对象,交给第三方容器来装配和创建。

    4.  DI的含义

         DI是一种手段,DI的含义为依赖注入,Unity提供三种方式分别是:属性注入、方法注入、构造函数注入。

     (二). 使用Unity来创建对象

     1. 创建对象的步骤

      ①:声明IUnityContainer容器。

      ②:通过RegisterType注册新类型 或者 通过RegisterInstance注册已经存在的类型。

      ③:通过Resove解析对象。

    特别注意:注册类型时,可以指定名称来注册,用于同时创建注册多个类型时,Resove可以通过指定的注册名称来针对指定解析。

     2.  代码说明

       ①:代码结构(后面所有的代码说明都是基于这个结构的)

     详细说明:各个类的内容待写。

     

     ②:直接创建简单对象(需要添加对Interface层和Service层的引用)

     1  {
     2                 Console.WriteLine("-------------------  二. Unity入门  -------------------");
     3                 Console.WriteLine("-------------------  01-直接创建对象  -------------------");
     4                 IUnityContainer container = new UnityContainer();
     5                 //进行注册,可以是:  接口-类、父类-子类、抽象类-子类。
     6                 //container.RegisterType<IPhone, AndroidPhone>();
     7                 //也可以用实例注册已经存在的对象
     8                 AndroidPhone android = new AndroidPhone();
     9                 container.RegisterInstance<IPhone>(android);
    10                 //解析对象
    11                 IPhone phone = container.Resolve<IPhone>();
    12                 phone.Call();
    13                 //我们会发现,虽然注册了AndroidPhone对象,但是该对象中的三个属性并没有实例化,依旧为空
    14                 Console.WriteLine("phone.iHeadphone==null? {0}", phone.iHeadphone == null);
    15                 Console.WriteLine("phone.iMicrophone==null? {0}", phone.iMicrophone == null);
    16                 Console.WriteLine("phone.iPower==null? {0}", phone.iPower == null);
    17  }

     详解:上述代码我们分别通过RegisterType注册类型、RegisterInstance注册已经存在的类型,我们会发现最终生成AndoridPhone中的三个属性并没有被实例化,那么怎么实例化里面的属性呢?这就涉及到后面的依赖注入了。

      ③. 指定命名来创建对象(需要添加对Interface层和Service层的引用)

     1    {
     2                 Console.WriteLine("-------------------  03-同时创建多个对象  -------------------");
     3                 //可以通过添加一个参数来区分
     4                 IUnityContainer container = new UnityContainer();
     5                 //注册类型时(指定命名)
     6                 container.RegisterType<IPhone, AndroidPhone>();
     7                 container.RegisterType<IPhone, ApplePhone>("apple");
     8                 container.RegisterType<IPhone, AndroidPhone>("android");
     9                 //依赖注入
    10                 container.RegisterType<IMicrophone, Microphone>();
    11                 container.RegisterType<IHeadphone, Headphone>();
    12                 container.RegisterType<IPower, Power>();
    13                 //解析对象
    14                 IPhone iphone1 = container.Resolve<IPhone>();
    15                 iphone1.Call();
    16                 //解析对象(指定命名)
    17                 IPhone iphone2 = container.Resolve<IPhone>("apple");
    18                 iphone2.Call();
    19                 IPhone iphone3 = container.Resolve<IPhone>("android");
    20                 iphone3.Call();
    21 
    22                 //获取所有已注册且指定命名的对象(未指定命名的不能获取)
    23                 var list = container.ResolveAll<IPhone>();
    24 
    25  }

      详解:注册类型时,如果默认不指定命名,则后面的会覆盖前面的,解析多个对象时,永远是最后一个注册的类型的对象。  所以RegisterType指定命名 、Resolve解析对象指定命名广泛用于同时创建多个对象的情况。

      我们发现上述代码有三句是依赖注入的,这个在下面讲解。

     (三). 依赖注入的三种形式

      Unity实现依赖注入有三种形式,分别是构造函数注入、属性注入、方法注入,分别对应三个特性:【InjectionConstructor】、【Dependency】、【InjectionMethod】,加上这三个特性表明构造函数中、属性中、方法中需要注入对象,使其实例化。

      方式一:属性注入。

      有WinPhone1类,实现了Iphone接口。其中WinPhone1类中的iMicrophone和iHeadphone两个属性上分别添加【Dependency】和【Dependency("fk")】,表明该属性对应的对象需要注入,其中iHeadphone需要Unity容器"指定命名"来注册类型。

    1  public interface IPhone
    2     {
    3         void Call();
    4         IMicrophone iMicrophone { get; set; }
    5 
    6         IHeadphone iHeadphone { get; set; }
    7 
    8         IPower iPower { get; set; }
    9     }
    View Code
     1    /// <summary>
     2     /// 测试属性注入的类
     3     /// </summary>
     4     public class WinPhone1 : IPhone
     5     {
     6         [Dependency("fk")]   //表示该属性需要注入,且需要指定名称
     7         public IMicrophone iMicrophone { get; set; }
     8         [Dependency]   //表示该属性需要注入
     9         public IHeadphone iHeadphone { get; set; }
    10         public IPower iPower { get; set; }
    11 
    12         public WinPhone1()
    13         {
    14             Console.WriteLine("{0}被构造", this.GetType().Name);
    15         }
    16 
    17         public void Call()
    18         {
    19             Console.WriteLine("{0}打电话", this.GetType().Name); ;
    20         }
    21     }
    View Code
     1                IUnityContainer container = new UnityContainer();
     2                 //方式一: 属性注入
     3                 Console.WriteLine("----------------------------方式一: 属性注入 -------------------------------");
     4                 container.RegisterType<IPhone, WinPhone1>();
     5                 //指定命名注入
     6                 container.RegisterType<IMicrophone, Microphone>("fk");
     7                 //普通注入
     8                 container.RegisterType<IHeadphone, Headphone>();
     9                 IPhone phone1 = container.Resolve<IPhone>();
    10                 phone1.Call();                
    View Code

     

    补充说明:我们发现Microphone和HeadPhone均被构造,说明这两个类被注入成功。

      方式二:方法注入。

      有WinPhone2类,实现了Iphone接口。其中在WinPhone2中的方法Init1234方法上加【InjectionMethod】特性,表示该方法中有对象需要注入。

     1    public class WinPhone2:IPhone
     2     {
     3         public IMicrophone iMicrophone { get; set; }
     4         public IHeadphone iHeadphone { get; set; }
     5         public IPower iPower { get; set; }
     6 
     7         public WinPhone2()
     8         {
     9             Console.WriteLine("{0}被构造", this.GetType().Name);
    10         }
    11 
    12         public void Call()
    13         {
    14             Console.WriteLine("{0}打电话", this.GetType().Name); ;
    15         }
    16 
    17         [InjectionMethod]   //方法注入,表明该方法中有对象需要注入
    18         public void Init1234(IPower power)
    19         {
    20             this.iPower = power;
    21         }
    22     }
    1        //方式二:方法注入
    2        Console.WriteLine("----------------------------方式二:方法注入 -------------------------------");
    3        container.RegisterType<IPhone, WinPhone2>();
    4        container.RegisterType<IPower, Power>();
    5        IPhone phone2 = container.Resolve<IPhone>();
    6        phone2.Call();            

    补充说明:我们发现Power被构造,说明该类被注入成功。

       方式三:构造函数注入(又分四种情况)

      A.  默认注入。 自动识别参数数量最多的构造函数,表示该构造函数中有对象需要注入。

      B. 指定构造函数。通过在构造函数上加 [InjectionConstructor]特性,表示该构造函数中有对象需要注入。

      C. 指定构造函数且指定注入参数的名称。通过在构造函数上加 [InjectionConstructor]特性,并且在参数上加 [Dependency("fk")] ,表示该构造函数中的该参数中的对象需要注入。

      D. 指定构造函数且指定注入参数的值(了解即可)。

     1     /// <summary>
     2     /// 测试构造函数注入(默认方式)
     3     /// </summary>
     4     public class WinPhone3 : IPhone
     5     {
     6         public IMicrophone iMicrophone { get; set; }
     7         public IHeadphone iHeadphone { get; set; }
     8         public IPower iPower { get; set; }
     9 
    10         public WinPhone3(IPower iPower)
    11         {
    12             Console.WriteLine("{0}被构造", this.GetType().Name);
    13         }
    14 
    15         public void Call()
    16         {
    17             Console.WriteLine("{0}打电话", this.GetType().Name); ;
    18         }
    19 
    20     }
     1     /// <summary>
     2     /// 测试构造函数注入(指定构造函数)
     3     /// </summary>
     4     public class WinPhone4 : IPhone
     5     {
     6         public IMicrophone iMicrophone { get; set; }
     7         public IHeadphone iHeadphone { get; set; }
     8         public IPower iPower { get; set; }
     9         public WinPhone4()
    10         {
    11 
    12         }
    13         [InjectionConstructor]   //指定该构造函数需要被注入
    14         public WinPhone4(IPower iPower)
    15         {
    16             Console.WriteLine("{0}被构造", this.GetType().Name);
    17         }
    18 
    19         public void Call()
    20         {
    21             Console.WriteLine("{0}打电话", this.GetType().Name); ;
    22         }
    23 
    24     }
     1     /// <summary>
     2     /// 测试构造函数注入(指定参数依赖的注册名称)
     3     /// </summary>
     4     public class WinPhone5 : IPhone
     5     {
     6         public IMicrophone iMicrophone { get; set; }
     7         public IHeadphone iHeadphone { get; set; }
     8         public IPower iPower { get; set; }
     9         public WinPhone5()
    10         {
    11 
    12         }
    13         [InjectionConstructor]   //指定该构造函数需要被注入且指定注入参数的名称
    14         public WinPhone5([Dependency("fk")]IPower iPower)
    15         {
    16             Console.WriteLine("{0}被构造", this.GetType().Name);
    17         }
    18 
    19         public void Call()
    20         {
    21             Console.WriteLine("{0}打电话", this.GetType().Name); ;
    22         }
    23 
    24     }
       /// <summary>
        /// 测试构造函数注入(指定参数值)
        /// </summary>
        public class WinPhone6 : IPhone
        {
            public IMicrophone iMicrophone { get; set; }
            public IHeadphone iHeadphone { get; set; }
            public IPower iPower { get; set; }
            public WinPhone6()
            {
    
            }
            [InjectionConstructor]   //指定该构造函数需要被注入且指定注入参数的值
            public WinPhone6(IPower iPower,string name)
            {
                Console.WriteLine("{0}被构造", this.GetType().Name);
            }
    
            public void Call()
            {
                Console.WriteLine("{0}打电话", this.GetType().Name); ;
            }
    
        }
     1                  //方式三:构造函数注入(又分4中情况)
     2                 /*
     3                  A. 默认方式
     4                  B. 指定构造函数
     5                  C. 指定参数依赖的注册名称
     6                  D. 指定参数值
     7                  */
     8                 Console.WriteLine("-----------------------方式三:构造函数注入(又分4中情况)----------------------------");
     9                 //A. 默认注入参数最多的构造函数
    10                 {
    11                     Console.WriteLine("-----------------------A. 默认注入参数最多的构造函数----------------------------");
    12                     container.RegisterType<IPhone, WinPhone3>();
    13                     container.RegisterType<IPower, Power>();
    14                     IPhone iphone = container.Resolve<IPhone>();
    15                     iphone.Call();
    16                 }
    17                 //B. 指定构造函数
    18                 {
    19                     Console.WriteLine("-----------------------B. 指定构造函数----------------------------");
    20                     container.RegisterType<IPhone, WinPhone4>();
    21                     container.RegisterType<IPower, Power>();
    22                     IPhone iphone = container.Resolve<IPhone>();
    23                     iphone.Call();
    24                 }
    25                 //C. 指定参数依赖的注册名称
    26                 {
    27                     Console.WriteLine("-----------------------C. 指定参数依赖的注册名称----------------------------");
    28                     container.RegisterType<IPhone, WinPhone5>();
    29                     container.RegisterType<IPower, Power>("fk");
    30                     IPhone iphone = container.Resolve<IPhone>();
    31                     iphone.Call();
    32                 }
    33                 //D.指定参数值(了解即可)
    34                 {
    35                     Console.WriteLine("-----------------------D.指定参数值(了解即可)----------------------------");
    36                     container.RegisterType<IPhone, WinPhone6>(new InjectionConstructor(new Power() { }, "ypf"));
    37                     IPhone iphone = container.Resolve<IPhone>();
    38                     iphone.Call();
    39                 }

      小结一下: 以上内容分别介绍了IOC和DI的概念、Unity的使用方式和创建对象的方式、DI的三种注入方式。细心的人或者有经验的人一定会发现,上述代码依旧需要添加对Service层和Interface层的引用,那么用Unity有什么用?使用了Untiy来创建对象有何好处呢?

      我们自己手写IOC的方式(反射+工厂+配置文件)的方式已经可以脱离对Service层的直接引用了,怎么引入Unity反而又需要添加对Service层的引用呢?这不是徒增烦恼么?

      下面我们来解惑:上述所有代码(包括Unity步骤的三走)都是在代码层次上,介绍Unity的基本API的使用,方便我们大家明白Unity有哪些功能,但在实际开发中,Unity三步走中的第二步,注册类型是通过配置文件来注册的,创建Unity容器和解析类型是通过代码来实现的→→这样就可以达到 脱离Service层的引用了(只需要把Service层的dll复制到主程序的bin文件下即可)。

      有的人还会发现:即使使用了配置文件最多也就是和手写iOC打了个平手,并没有看到Unity特有的好处,我们下个章节将介绍的Unity的声明周期,即是Unity特有的一些封装,就可以见识到Unity的强大。

     

  • 相关阅读:
    批处理中的符号
    IF命令详解
    批处理介绍
    批处理中的变量
    单调栈2289. 使数组按非递减顺序排列
    01BFS2290. 到达角落需要移除障碍物的最小数目
    电脑FQ成功后,命令行无法FQ
    d365 webresource 批量更新
    asp.net mvc传值方式
    SpringBoot整合Nacos实现动态配置数据源
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/7476723.html
Copyright © 2020-2023  润新知