01-Unity深入浅出(一)
一. 温故而知新
在开始学习Unity框架之前,有必要温习一下 【依赖倒置原则】和【手写IOC】, 因为我们框架代码的构建都是基于【依赖倒置原则】的,而Unity框架的核心思想就是IOC和DI,所以有必要了解通过【反射+工厂+配置文件】来实现手写IOC的原理。
废不多说,简单提炼一下依赖原则的核心理念:面向接口编程。上层(UI层)不直接依赖于下层,而是依赖于下层对应的接口,下层去实现该接口。
经典案例:业务层连接数据库,依赖接口,可以实现不改代码轻松切换数据库的目的。
手动创建对象的三个阶段:直接new实例化→面向接口编程→将创建对象的交给工厂【手写IOC】。
#region 手动创建对象几个阶段
{
Console.WriteLine("--------------一.复习依赖倒置原则和手写IOC(简单工厂+反射+配置文件)----------------");
//1. 直接创建(需要添加对Service层的引用)
Console.WriteLine("--------------1. 直接创建----------------");
AndroidPhone androidPhone1 = new AndroidPhone();
androidPhone1.Call(); //2. 利用接口进行改造(需要添加对Service和Interface层的引用)
Console.WriteLine("--------------2. 利用接口进行改造----------------");
IPhone androidPhone2 = new AndroidPhone();
androidPhone2.Call(); //3. 手写IOC(只需要添加Interface层的引用,但需要把Service层生成的DLL拷贝到该项目bin下)
Console.WriteLine("--------------3. 利用接口进行改造----------------");
IPhone androidPhone3 = ObjectFactory.CreateObject<IPhone>();
androidPhone3.Call(); //总结:第三个阶段基本上已经是我们自己写容器的最高层次
//下面我们将借助微软的第三方容器Unity来创建 }
#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层的引用)
{
Console.WriteLine("------------------- 二. Unity入门 -------------------");
Console.WriteLine("------------------- 01-直接创建对象 -------------------");
IUnityContainer container = new UnityContainer();
//进行注册,可以是: 接口-类、父类-子类、抽象类-子类。
//container.RegisterType<IPhone, AndroidPhone>();
//也可以用实例注册已经存在的对象
AndroidPhone android = new AndroidPhone();
container.RegisterInstance<IPhone>(android);
//解析对象
IPhone phone = container.Resolve<IPhone>();
phone.Call();
//我们会发现,虽然注册了AndroidPhone对象,但是该对象中的三个属性并没有实例化,依旧为空
Console.WriteLine("phone.iHeadphone==null? {0}", phone.iHeadphone == null);
Console.WriteLine("phone.iMicrophone==null? {0}", phone.iMicrophone == null);
Console.WriteLine("phone.iPower==null? {0}", phone.iPower == null);
}
详解:上述代码我们分别通过RegisterType注册类型、RegisterInstance注册已经存在的类型,我们会发现最终生成AndoridPhone中的三个属性并没有被实例化,那么怎么实例化里面的属性呢?这就涉及到后面的依赖注入了。
③. 指定命名来创建对象(需要添加对Interface层和Service层的引用)
{
Console.WriteLine("------------------- 03-同时创建多个对象 -------------------");
//可以通过添加一个参数来区分
IUnityContainer container = new UnityContainer();
//注册类型时(指定命名)
container.RegisterType<IPhone, AndroidPhone>();
container.RegisterType<IPhone, ApplePhone>("apple");
container.RegisterType<IPhone, AndroidPhone>("android");
//依赖注入
container.RegisterType<IMicrophone, Microphone>();
container.RegisterType<IHeadphone, Headphone>();
container.RegisterType<IPower, Power>();
//解析对象
IPhone iphone1 = container.Resolve<IPhone>();
iphone1.Call();
//解析对象(指定命名)
IPhone iphone2 = container.Resolve<IPhone>("apple");
iphone2.Call();
IPhone iphone3 = container.Resolve<IPhone>("android");
iphone3.Call(); //获取所有已注册且指定命名的对象(未指定命名的不能获取)
var list = container.ResolveAll<IPhone>(); }
详解:注册类型时,如果默认不指定命名,则后面的会覆盖前面的,解析多个对象时,永远是最后一个注册的类型的对象。 所以RegisterType指定命名 、Resolve解析对象指定命名广泛用于同时创建多个对象的情况。
我们发现上述代码有三句是依赖注入的,这个在下面讲解。
(三). 依赖注入的三种形式
Unity实现依赖注入有三种形式,分别是构造函数注入、属性注入、方法注入,分别对应三个特性:【InjectionConstructor】、【Dependency】、【InjectionMethod】,加上这三个特性表明构造函数中、属性中、方法中需要注入对象,使其实例化。
方式一:属性注入。
有WinPhone1类,实现了Iphone接口。其中WinPhone1类中的iMicrophone和iHeadphone两个属性上分别添加【Dependency】和【Dependency("fk")】,表明该属性对应的对象需要注入,其中iHeadphone需要Unity容器"指定命名"来注册类型。
public interface IPhone
{
void Call();
IMicrophone iMicrophone { get; set; } IHeadphone iHeadphone { get; set; } IPower iPower { get; set; }
}
/// <summary>
/// 测试属性注入的类
/// </summary>
public class WinPhone1 : IPhone
{
[Dependency("fk")] //表示该属性需要注入,且需要指定名称
public IMicrophone iMicrophone { get; set; }
[Dependency] //表示该属性需要注入
public IHeadphone iHeadphone { get; set; }
public IPower iPower { get; set; } public WinPhone1()
{
Console.WriteLine("{0}被构造", this.GetType().Name);
} public void Call()
{
Console.WriteLine("{0}打电话", this.GetType().Name); ;
}
}
IUnityContainer container = new UnityContainer();
//方式一: 属性注入
Console.WriteLine("----------------------------方式一: 属性注入 -------------------------------");
container.RegisterType<IPhone, WinPhone1>();
//指定命名注入
container.RegisterType<IMicrophone, Microphone>("fk");
//普通注入
container.RegisterType<IHeadphone, Headphone>();
IPhone phone1 = container.Resolve<IPhone>();
phone1.Call();
补充说明:我们发现Microphone和HeadPhone均被构造,说明这两个类被注入成功。
方式二:方法注入。
有WinPhone2类,实现了Iphone接口。其中在WinPhone2中的方法Init1234方法上加【InjectionMethod】特性,表示该方法中有对象需要注入。
public class WinPhone2:IPhone
{
public IMicrophone iMicrophone { get; set; }
public IHeadphone iHeadphone { get; set; }
public IPower iPower { get; set; } public WinPhone2()
{
Console.WriteLine("{0}被构造", this.GetType().Name);
} public void Call()
{
Console.WriteLine("{0}打电话", this.GetType().Name); ;
} [InjectionMethod] //方法注入,表明该方法中有对象需要注入
public void Init1234(IPower power)
{
this.iPower = power;
}
}
//方式二:方法注入
Console.WriteLine("----------------------------方式二:方法注入 -------------------------------");
container.RegisterType<IPhone, WinPhone2>();
container.RegisterType<IPower, Power>();
IPhone phone2 = container.Resolve<IPhone>();
phone2.Call();
补充说明:我们发现Power被构造,说明该类被注入成功。
方式三:构造函数注入(又分四种情况)
A. 默认注入。 自动识别参数数量最多的构造函数,表示该构造函数中有对象需要注入。
B. 指定构造函数。通过在构造函数上加 [InjectionConstructor]特性,表示该构造函数中有对象需要注入。
C. 指定构造函数且指定注入参数的名称。通过在构造函数上加 [InjectionConstructor]特性,并且在参数上加 [Dependency("fk")] ,表示该构造函数中的该参数中的对象需要注入。
D. 指定构造函数且指定注入参数的值(了解即可)。
/// <summary>
/// 测试构造函数注入(默认方式)
/// </summary>
public class WinPhone3 : IPhone
{
public IMicrophone iMicrophone { get; set; }
public IHeadphone iHeadphone { get; set; }
public IPower iPower { get; set; } public WinPhone3(IPower iPower)
{
Console.WriteLine("{0}被构造", this.GetType().Name);
} public void Call()
{
Console.WriteLine("{0}打电话", this.GetType().Name); ;
} }
/// <summary>
/// 测试构造函数注入(指定构造函数)
/// </summary>
public class WinPhone4 : IPhone
{
public IMicrophone iMicrophone { get; set; }
public IHeadphone iHeadphone { get; set; }
public IPower iPower { get; set; }
public WinPhone4()
{ }
[InjectionConstructor] //指定该构造函数需要被注入
public WinPhone4(IPower iPower)
{
Console.WriteLine("{0}被构造", this.GetType().Name);
} public void Call()
{
Console.WriteLine("{0}打电话", this.GetType().Name); ;
} }
/// <summary>
/// 测试构造函数注入(指定参数依赖的注册名称)
/// </summary>
public class WinPhone5 : IPhone
{
public IMicrophone iMicrophone { get; set; }
public IHeadphone iHeadphone { get; set; }
public IPower iPower { get; set; }
public WinPhone5()
{ }
[InjectionConstructor] //指定该构造函数需要被注入且指定注入参数的名称
public WinPhone5([Dependency("fk")]IPower iPower)
{
Console.WriteLine("{0}被构造", this.GetType().Name);
} public void Call()
{
Console.WriteLine("{0}打电话", this.GetType().Name); ;
} }
/// <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); ;
} }
//方式三:构造函数注入(又分4中情况)
/*
A. 默认方式
B. 指定构造函数
C. 指定参数依赖的注册名称
D. 指定参数值
*/
Console.WriteLine("-----------------------方式三:构造函数注入(又分4中情况)----------------------------");
//A. 默认注入参数最多的构造函数
{
Console.WriteLine("-----------------------A. 默认注入参数最多的构造函数----------------------------");
container.RegisterType<IPhone, WinPhone3>();
container.RegisterType<IPower, Power>();
IPhone iphone = container.Resolve<IPhone>();
iphone.Call();
}
//B. 指定构造函数
{
Console.WriteLine("-----------------------B. 指定构造函数----------------------------");
container.RegisterType<IPhone, WinPhone4>();
container.RegisterType<IPower, Power>();
IPhone iphone = container.Resolve<IPhone>();
iphone.Call();
}
//C. 指定参数依赖的注册名称
{
Console.WriteLine("-----------------------C. 指定参数依赖的注册名称----------------------------");
container.RegisterType<IPhone, WinPhone5>();
container.RegisterType<IPower, Power>("fk");
IPhone iphone = container.Resolve<IPhone>();
iphone.Call();
}
//D.指定参数值(了解即可)
{
Console.WriteLine("-----------------------D.指定参数值(了解即可)----------------------------");
container.RegisterType<IPhone, WinPhone6>(new InjectionConstructor(new Power() { }, "ypf"));
IPhone iphone = container.Resolve<IPhone>();
iphone.Call();
}
小结一下: 以上内容分别介绍了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的强大。
01-Unity深入浅出(一)的更多相关文章
- 第十节:基于MVC5+Unity+EF+Log4Net的基础结构搭建
一. 前言 本节继续探讨一种新的框架搭建模式,框架的结构划分和上一节是相同的,本节IOC框架换成了Unity,并且采用构造函数注入的方式,另外服务层的封装模式也发生了变化,下面将详细的进行探讨. (一 ...
- Unity制作王者荣耀商业级手游
<王者荣耀>这种现象级手机游戏是如何制作出来的呢?本文以<王者荣耀>MOBO类型的多人在线战术竞技游戏为入口,覆盖Unity游戏制作开发前端与Node.js服务器端的开发必备知 ...
- Kafka .net 开发入门
Kafka安装 首先我们需要在windows服务器上安装kafka以及zookeeper,有关zookeeper的介绍将会在后续进行讲解. 在网上可以找到相应的安装方式,我采用的是腾讯云服务器,借鉴的 ...
- 深入浅出!从语义角度分析隐藏在Unity协程背后的原理
Unity的协程使用起来比较方便,但是由于其封装和隐藏了太多细节,使其看起来比较神秘.比如协程是否是真正的异步执行?协程与线程到底是什么关系?本文将从语义角度来分析隐藏在协程背后的原理,并使用C++来 ...
- 深入浅出HTTP请求(转自http://www.cnblogs.com/yin-jingyu/archive/2011/08/01/2123548.html)
HTTP(HyperText Transfer Protocol)是一套计算机通过网络进行通信的规则.计算机专家设计出HTTP,使HTTP客户(如Web浏览器)能够从HTTP服务器(Web服务 器)请 ...
- [Unity优化]批处理01:Statistics窗口
参考链接: https://docs.unity3d.com/Manual/RenderingStatistics.html unity版本:2018.3.8 新建一个场景,只保留Main Camer ...
- [Unity动画]01.HasExitTime & ApplyRootMotion
参考链接: https://www.cnblogs.com/hammerc/p/4828774.html 资源下载: https://assetstore.unity.com/packages/ess ...
- Unity进阶:行为树 01
版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...
- 01 Hello Unity
Unity接界面布局: Hierarchy(层级视图):存放当前游戏场景内所有游戏物体 Scense(主界面):主要的游戏设计界面,开发者操作界面 Game(游戏视图):玩家视角,也是摄像机所要看到的 ...
随机推荐
- MT【267】第一次很重要
\begin{equation*}\textbf{已知}x_1,x_2<\pi,x_{n+1}=x_n+\left\{ \begin{aligned} sin x_n &,x_n> ...
- 【cf789D】Weird journey(欧拉路、计数)
cf788B/789D. Weird journey 题意 n个点m条边无重边有自环无向图,问有多少种路径可以经过m-2条边两次,其它两条边1次.边集不同的路径就是不同的. 题解 将所有非自环的边变成 ...
- js 时间类函数
js 时间类是 Date() var currtime = new Date();// 实例一个时间,是当前时间 接收一个时间戳为参数 var time2=new Date(currtime.get ...
- nginx+腾讯云免费ssl证书+阿里云ECS实现Https配置
1. 申请SSL证书: 首先我们需要到腾讯云那边申请一个ssl证书,对于个人博客类型的,建议采用免费版本,土豪除外 申请地址:https://console.cloud.tencent.com/ssl ...
- [ZJOI2018]历史
[ZJOI2018]历史 最大化access轻重链的切换次数 考虑一个点的贡献,即它交换重儿子的次数 发现这个次数只和它自己ai以及每个儿子的子树次数和有关. 一个关键的事实是: 我们可以自上而下进行 ...
- vue2.0项目实战(1)基础入门
最近公司的H5项目准备重构,部门老大说前端使用vue2.0来开发,所以就准备把整个项目的开发过程记录下来,一方面是为了记录开发过程中遇到的坑,另一方面也加强自己写作的能力. 什么是 Vue? 简要介绍 ...
- c# WebApi之解决跨域问题:Cors
什么是跨域问题 出于安全考虑,浏览器会限制脚本中发起的跨站请求,浏览器要求JavaScript或Cookie只能访问同域下的内容.由于这个原因,我们不同站点之间的数据访问会被拒绝. Cors解决跨域问 ...
- (转)java中引用传递和值传递
https://blog.csdn.net/javazejian/article/details/51192130 https://www.cnblogs.com/perfy/archive/2012 ...
- win32-api: 让 static 控件中的水平横行,垂直居中。
CreateWindowEx(....., SS_CENTER | SS_CENTERIMAGE); SS_CENTER 能让文字水平居中. SS_CENTERIMAGE 能让文字垂直居中. htt ...
- bzoj1791[IOI2008]Island岛屿(基环树+DP)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1791 题目大意:给你一棵n条边的基环树森林,要你求出所有基环树/树的直径之和.n< ...