01-Unity深刻浅出(一)

一. 温故而知新html

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

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

  经典案例:业务层链接数据库,依赖接口,能够实现不改代码轻松切换数据库的目的。编程

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

 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.htmliphone

二. Unity深刻浅出ide

 

 (一). 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的强大。