Unity 依赖注入
关于Ioc的框架有很多,比如astle Windsor、Unity、Spring.NET、StructureMap,我们这边使用微软提供的Unity做示例,你可以使用Nuget添加Unity,也可以引用Microsoft.Practices.Unity.dll和Microsoft.Practices.Unity.Configuration.dll,下面我们就一步一步的学习下Unity依赖注入的详细使用。如果不明白什么是控制反转和依赖注入,请参考控制反转和依赖注入模式
下面通过一个示例来讲解Unity不同的依赖注入,现在有一家公司,这家公司有很多的员工,这些员工分别来自不同的省份,有的是浙江人,有的是四川人,也有的是湖南人等等,因为公司上了一定的规模,所以为了解决员工的吃饭问题,所以公司决定built一个食堂,但是不同地方的员工的口味不同,所以食堂必须具备烹饪不同菜系的功能,ok,接下来就围绕这这个例子来讲解Unity的依赖注入。
1、构造器注入
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 向容器中注册需要通过容器生成的对象
UnityContainer的实例方法:Resolve 设置生成的对象的注入目标(就是设置生成的对象需要注入哪个目标)
2、属性注入-通过Dependency特性
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特性声明需要外部注入依赖的属性,注:该特性
3、方法注入-通过InjectionMethod特性
方法注入和属性方式使用一样,方法注入只需要在方法前加[InjectionMethod]标记就行了从方法注入的定义上看,只是模糊的说对某个方法注入,但是方法注入无非三种:
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());
}
}
}
4、配置文件配置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());
}
}
}
输出:
5、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容器会返回上次创建的实例,而不会重新创建一个实例,这也是单例的精髓之处,但是具体的实例销毁时间,可能是容器销毁的时候,也可能是应用程序销毁的时候,具体我也不是很清楚.
- 张兴华:云端架构助力企业快速成长
- 腾讯云GAME-TECH沙龙干货回顾:腾讯游戏云全球化实践
- 基于内容关键性的高效 FEC 抗网络丢包算法
- 腾讯云GAME-TECH沙龙干货回顾:三七互娱游戏全球化运营心得分享
- 《实现模式》读书总结
- 腾讯云 GAME-TECH 沙龙干货回顾:Eyougame 海外发行实践分享
- 腾讯 AI Lab 副主任俞栋:过去两年基于深度学习的声学模型进展
- 白辉(七公):电商转型难题,看云如何破题
- 《Node.js在CLI下的工程化体系实践》成都OSC源创会分享总结
- 5分钟教你玩转 sklearn 机器学习(上)
- WebSocket系列之socket.io
- Python 数据分析学习笔记
- ERP管理软件系统设计方案
- 腾讯云GAME-TECH沙龙——全球同服游戏语音解决方案
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- python基本数据类型
- Python 技术篇-PyQt5动画功能演示,组件移动、尺寸改变动画演示
- 搭建k8s高可用集群 - 二进制方式
- DeepWalk:图网络与NLP的巧妙融合
- 基于kubeadm搭建k8s高可用集群
- 扑克牌例题与Collections工具类
- 快速了解Kubernetes
- PyQt5 技巧篇-按钮竖排显示方法,Qt Designer设置按钮竖排显示。
- Kafka集群监控、安全机制与最佳实践
- PyQt5 技术篇-设置输入框的placeholder方法,Qt Designer设置Line Edit、Text Edit编辑框的placeholder
- Kafka集群搭建及必知必会
- Python 技术篇-打开指定文件夹、目录、路径方法,运行指定文件演示
- Python 技巧篇-同一个方法多次引用不同效果功能实现,可选参数设置方法
- Kafka核心API——Connect API
- PyQt5 技术篇-设置alignment对齐方式。Qt Designer设置文本对齐方式。居中、左对齐、右对齐、上对齐、下对齐。