IOC容器:Unity
一、什么是IOC
学习IOC之前先来了解一个依赖导致原则(DIP),依赖导致原则是IOC的核心原理。
依赖导致:即上层模块不应该依赖于低层模块,二者应该通过抽象来依赖。依赖于抽象,而不是依赖于细节。
首先来看下面的例子:
1、定义一个接口,封装数据库的基本CRUD操作,接口定义如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data; namespace DataBase.Interface
{
/// <summary>
/// 数据访问接口
/// </summary>
public interface IDbInterface
{
string Insert();
string Delete();
string Update();
string Query();
}
}
2、定义一个MSSQL类实现该接口,用来模仿SQLServer操作,MSSQL类定义如下:
using DataBase.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace DataBase.MSSQL
{
public class DbMSSQL : IDbInterface
{
public string Delete()
{
return "MSSQL执行删除";
} public string Insert()
{
return "MSSQL执行插入";
} public string Query()
{
return "MSSQL执行查询";
} public string Update()
{
return "MSSQL执行更新";
}
}
}
3、定义一个Oracle类实现该接口,模仿Oracle的操作,Oracle类定义如下:
using DataBase.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace DataBase.Oracle
{
public class DbOracle : IDbInterface
{
public string Delete()
{
return "Oracle执行删除";
} public string Insert()
{
return "Oracle执行插入";
} public string Query()
{
return "Oracle执行查询";
} public string Update()
{
return "Oracle执行更新";
}
}
}
4、定义一个控制台应用程序来调用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataBase.Interface;
using DataBase.MSSQL; namespace IOCConApp
{
class Program
{
static void Main(string[] args)
{
// 常规做法,即程序的上端,依赖于下端,依赖于细节
DbMSSQL mssql = new DbMSSQL();
}
}
}
常规做法是添加引用,然后直接实例化类,但是这样会依赖于细节实现,现将代码修改如下:
// 通过抽象来依赖
IDbInterface dbInterface = new DbMSSQL();
但是这样修改以后,虽然左边是抽象了,但是右边还是依赖于细节。
那就究竟什么是IOC呢?
IOC(Inversion of Control)即控制反转,是一个重要的面向对象编程的法则来消减程序之间的耦合问题,把程序中上层对下层依赖,转移到一个第三方容器中来装配。IOC是程序设计的目标,实现方式包含依赖注入和依赖查找,在.net中只有依赖注入。
说到IOC,就不能不说DI。DI:即依赖注入,是IOC的实现手段。
二、使用Unity实现IOC
Unity是一个IoC容器,用来实现依赖注入(Dependency Injection,DI),减少耦合的,Unity出自于伟大的微软。
unity组件网址:http://unity.codeplex.com/
unity能够做什么呢,列举部分如下:
1.Unity支持简单对象创建,特别是分层对象结构和依赖,以简化程序代码。其包含一个编译那些可能存在依赖于其他对象的对象实例机制。
2.Unity支持必要的抽象,其允许开发者在运行时或配置去指定依赖关系同时可以简单的管理横切点(AOP)。
3.Unity增加了推迟到容器组件配置的灵活性。其同样支持一个容器层次的结构。
4.Unity拥有服务定位能力,对于一个程序在许多情况下重复使用组件来分离和集中功能是非常有用的。
5.Unity允许客户端储存或缓存容器。对于在ASP.NET Web applications中开发者将容器持久化于ASP.NET中的session或application中特别有效。
6.Unity拥有拦截能力,其允许开发者通过创建并执行handlers(在方法或属性被调用到达之前)来为已存在的组件增加一个函数,并再次为返回调用结果。
7.Unity可以从标准配置系统中读取配置信息,例如:XML文件,同时使用配置文件来配置容器。
8.Unity支持开发者实现自定义容器扩展,例如:你可以实现方法来允许额外的对象构造和容器特征,例如缓存。
9.Unity允许架构师和开发者在现代化的程序中更简单的实现通用设计模式。
什么情况下要使用unity呢?
1.所构建的系统依赖于健全的面向对象原则,但是大量不同的代码交织在一起而难以维护。
2.构建的对象和类需要依赖其他对象或类。
3.依赖于复杂的或需要抽象的对象。
4.希望利用构造函数、方法或属性的调用注入优势。
5.希望管理对象实例的生命周期。
6.希望能够在运行时管理并改变依赖关系。
7.希望在拦截方法或属性调用的时候生成一个策略链或管道处理容器来实现横切(AOP)任务。
8.希望在Web Application中的回发操作时能够缓存或持久化依赖关系。
1、程序中安装Unity
使用管理NuGet程序包来安装Unity,在项目上右键,选择管理NuGet程序包:
在搜索框里面输入Unity,点击右侧安装按钮进行安装:
出现以下信息表示安装成功:
2、使用Unity实现DI
先来看看最简单的Unity实现方式:
IUnityContainer container = new UnityContainer();//1、定义一个空容器
container.RegisterType<IDbInterface, DbMSSQL>();//2、注册类型,表示遇到IDbInterface的类型,创建DbMSSQL的实例
var db = container.Resolve<IDbInterface>();
Console.WriteLine(db.Insert());
Console.ReadKey();
结果:
从结果中可以看出,db是DbMSSQL类型的实例。
除了使用RegisterType注册类型以外,还可以注册一个实例,例如:
// 使用RegisterInstance注册IDbInterface的实例:new DbMSSQL()
container.RegisterInstance<IDbInterface>(new DbMSSQL());
3、三种注入方式
三种注入方式:构造函数注入、属性注入、方法注入。
3.1 定义IHeadphone接口,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace DataBase.Interface
{
public interface IHeadphone
{ }
}
3.2 定义IMicrophone接口,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace DataBase.Interface
{
public interface IMicrophone
{ }
}
3.3 定义IPower接口,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace DataBase.Interface
{
public interface IPower
{ }
}
3.4 定义IPhone接口,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace DataBase.Interface
{
public interface IPhone
{
void Call();
IMicrophone iMicrophone { get; set; }
IHeadphone iHeadphone { get; set; }
IPower iPower { get; set; }
}
}
3.5 分别实现上面定义的接口
IPhone接口的实现如下:
using DataBase.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.Attributes; namespace DataBase.MSSQL
{
public class ApplePhone : IPhone
{
[Dependency]//属性注入
public IMicrophone iMicrophone { get; set; }
public IHeadphone iHeadphone { get; set; }
public IPower iPower { get; set; } [InjectionConstructor]//构造函数注入
public ApplePhone(IHeadphone headphone)
{
this.iHeadphone = headphone;
Console.WriteLine("{0}带参数构造函数", this.GetType().Name);
} public void Call()
{
Console.WriteLine("{0}打电话", this.GetType().Name); ;
} [InjectionMethod]//方法注入
public void Init1234(IPower power)
{
this.iPower = power;
}
}
}
IHeadphone接口的实现如下:
using DataBase.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace DataBase.MSSQL
{
public class Headphone : IHeadphone
{
public Headphone()
{
Console.WriteLine("Headphone 被构造");
}
}
}
IMicrophone接口的实现如下:
using DataBase.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace DataBase.MSSQL
{
public class Microphone : IMicrophone
{
public Microphone()
{
Console.WriteLine("Microphone 被构造");
}
}
}
IPower接口的实现如下:
using DataBase.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace DataBase.MSSQL
{
public class Power : IPower
{
public Power()
{
Console.WriteLine("Power 被构造");
}
}
}
控制台程序调用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataBase.Interface;
using DataBase.MSSQL;
using Unity; namespace IOCConApp
{
/// <summary>
/// IOC():控制反转,把程序上层对下层的依赖,转移到第三方的容器来装配
/// 是程序设计的目标,实现方式包含了依赖注入和依赖查找(.net里面只有依赖注入)
/// DI:依赖注入,是IOC的实习方式。
/// </summary>
class Program
{
static void Main(string[] args)
{
#region MyRegion
//// 常规做法,即程序的上端,依赖于下端,依赖于细节
//DbMSSQL mssql = new DbMSSQL(); //// 通过抽象来依赖
//IDbInterface dbInterface = new DbMSSQL(); //IUnityContainer container = new UnityContainer();//1、定义一个空容器
//container.RegisterType<IDbInterface, DbMSSQL>();//2、注册类型,表示遇到IDbInterface的类型,创建DbMSSQL的实例
//var db = container.Resolve<IDbInterface>(); //// 使用RegisterInstance注册IDbInterface的实例:new DbMSSQL()
//container.RegisterInstance<IDbInterface>(new DbMSSQL());
//Console.WriteLine(db.Insert());
#endregion IUnityContainer container = new UnityContainer();
container.RegisterType<IPhone, ApplePhone>();
container.RegisterType<IMicrophone, Microphone>();
container.RegisterType<IHeadphone, Headphone>();
container.RegisterType<IPower, Power>(); IPhone phone = container.Resolve<IPhone>(); Console.WriteLine($"phone.iHeadphone==null? {phone.iHeadphone == null}");
Console.WriteLine($"phone.iMicrophone==null? {phone.iMicrophone == null}");
Console.WriteLine($"phone.iPower==null? {phone.iPower == null}"); Console.ReadKey();
}
}
}
输出结果:
从输出结果中可以看出三种注入方式的执行顺序:先执行构造函数注入,在执行属性注入,最后执行方法注入。
注意:默认情况下如果构造函数上面没有使用特性,那么默认找参数最多的构造函数执行注入。
4、一个接口多个实现进行注册
如果多个不同的实例实现同一个接口,这种情况该怎么注册呢?先来看看下面的代码:
IUnityContainer container = new UnityContainer();//1、定义一个空容器
container.RegisterType<IDbInterface, DbMSSQL>();//2、注册类型,表示遇到IDbInterface的类型,创建DbMSSQL的实例
container.RegisterType<IDbInterface, DbOracle>();//表示遇到IDbInterface的类型,创建DbMSSQL的实例
var db = container.Resolve<IDbInterface>();
Console.WriteLine(db.Insert());
运行结果:
从运行结果中可以看出,后面注册的类型会把前面注册的类型给覆盖掉,那么该如何解决呢?可以通过参数的方式来解决,代码如下:
IUnityContainer container = new UnityContainer();//1、定义一个空容器
container.RegisterType<IDbInterface, DbMSSQL>("sql");//2、注册类型,表示遇到IDbInterface的类型,创建DbMSSQL的实例
container.RegisterType<IDbInterface, DbOracle>("oracle");//表示遇到IDbInterface的类型,创建DbMSSQL的实例
var sql = container.Resolve<IDbInterface>("sql");
var oracle = container.Resolve<IDbInterface>("oracle");
Console.WriteLine(sql.Insert());
Console.WriteLine(oracle.Insert());
运行结果:
5、生命周期
生命周期及一个对象从创建到释放中间经过的时间。先看下面的代码:
IUnityContainer container = new UnityContainer();
container.RegisterType<IDbInterface, DbMSSQL>();
IDbInterface db1 = container.Resolve<IDbInterface>();
IDbInterface db2 = container.Resolve<IDbInterface>();
Console.WriteLine("HashCode:"+db1.GetHashCode().ToString());
Console.WriteLine("HashCode:" + db2.GetHashCode().ToString());
Console.WriteLine(object.ReferenceEquals(db1,db2));
结果:
表明db1和db2是两个不同的实例,即默认情况下生命周期是瞬时的,每次都是创建一个新的实例。
container.RegisterType<IDbInterface, DbMSSQL>(new TransientLifetimeManager());表示是瞬时生命周期,默认情况下即这种。
在看下面的代码:
IUnityContainer container = new UnityContainer();
container.RegisterType<IDbInterface, DbMSSQL>(new ContainerControlledLifetimeManager());
IDbInterface db1 = container.Resolve<IDbInterface>();
IDbInterface db2 = container.Resolve<IDbInterface>();
Console.WriteLine("HashCode:" + db1.GetHashCode().ToString());
Console.WriteLine("HashCode:" + db2.GetHashCode().ToString());
Console.WriteLine(object.ReferenceEquals(db1, db2));
结果:
上图的结果可以看出,db1和db2是同一个实例。
container.RegisterType<IDbInterface, DbMSSQL>(new ContainerControlledLifetimeManager())表示是容器单例,每次都是同一个实例。
6、使用配置文件实现
在上面的例子中,所有的例子都是一直在依赖于细节,那么怎么解决不依赖于细节呢?答案是只能使用配置文件,配置文件如下:
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
</configSections>
<unity>
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
<containers>
<container name="testContainer">
<!--逗号前面是接口类型的完全限定名:命名空间+接口名称,逗号后面是DLL文件的名称 name解决同一个接口不同实例问题-->
<register type="DataBase.Interface.IDbInterface,DataBase.Interface" mapTo="DataBase.MSSQL.DbMSSQL, DataBase.MSSQL" name="sql"/>
<register type="DataBase.Interface.IDbInterface,DataBase.Interface" mapTo="DataBase.Oracle.DbOracle, DataBase.Oracle" name="oracle"/>
</container>
</containers>
</unity>
</configuration>
注意:这个一个单独的配置文件,要把属性里面的复制到输出目录改为始终复制,那么这个配置文件才会生成到Debug目录里面。
程序如下:
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\Unity.Config");//找配置文件的路径
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
IUnityContainer container = new UnityContainer();
section.Configure(container, "testContainer");
IDbInterface db = container.Resolve<IDbInterface>("sql");
Console.WriteLine(db.Insert());
结果:
观察上面的代码,会发现,如果改成使用配置文件的方式实现的话,代码里面就不会依赖于细节了,只要一个接口类型。既然没有细节了,那么对项目进行如下的改造:把引用里面对细节的引用都去掉(即去掉DataBase.MSSQL和DataBase.Oracle),然后Debug文件夹里面没有这两个DLL了,但是这时需要把这两个DLL复制到Debug目录下面,否则程序运行的时候会找不到具体实现的类型。这样就意味着程序架构只依赖于接口。
引用里面只要对接口的引用了,没有对具体实现的引用。去掉了对细节的依赖。
注意:使用配置文件实现时,必须把接口的具体实现类复制到程序目录下面。
如果有额外添加了一种数据库,那么只需要修改配置文件,把新的实现类复制到程序目录下面即可实现程序的升级。
使用配置文件的时候,需要把UnityContainer容器定义为静态的,这样只需要读取一次配置文件即可。
IOC容器:Unity的更多相关文章
- IOC容器Unity的使用及独立配置文件Unity.Config
[本段摘录自:IOC容器Unity 使用http://blog.csdn.net/gdjlc/article/details/8695266] 面向接口实现有很多好处,可以提供不同灵活的子类实现,增加 ...
- [IoC容器Unity]第四回:使用范例
1.引言 前面几个章节介绍了Unity的基本使用,主要分为程序和配置文件两种方法的使用,可以参考一下链接, [IoC容器Unity]第一回:Unity预览 [IoC容器Unity]第二回:Lifeti ...
- [IoC容器Unity] :Unity预览
1.引言 高内聚,低耦合成为一个OO架构设计的一个参考标准.高内聚是一个模块或者一个类中成员跟这个模块或者类的关系尽量高,低耦合是不同模块或者不同类之间关系尽量简单. 拿咱国家举例来说,假如你是中国人 ...
- [IoC容器Unity]第一回:Unity预览
1.引言 高内聚,低耦合成为一个OO架构设计的一个参考标准.高内聚是一个模块或者一个类中成员跟这个模块或者类的关系尽量高,低耦合是不同模块或者不同类之间关系尽量简单. 拿咱国家举例来说,假如你是中国人 ...
- [IoC容器Unity]第三回:依赖注入
1.引言 上节介绍了,Unity的Lifetime Managers生命周期,Unity具体实现依赖注入包含构造函数注入.属性注入.方法注入,所谓注入相当赋值,下面一个一个来介绍. 2.构造函数注入 ...
- [IoC容器Unity]第二回:Lifetime Managers生命周期
1.引言 Unity的生命周期是注册的类型对象的生命周期,而Unity默认情况下会自动帮我们维护好这些对象的生命周期,我们也可以显示配置对象的生命周期,Unity将按照配置自动管理,非常方便,下面就介 ...
- [转载][IoC容器Unity]第二回:Lifetime Managers生命周期
1.引言 Unity的生命周期是注册的类型对象的生命周期,而Unity默认情况下会自动帮我们维护好这些对象的生命周期,我们也可以显示配置对象的生命周期,Unity将按照配置自动管理,非常方便,下面就介 ...
- 微软IOC容器Unity简单代码示例3-基于约定的自动注册机制
@(编程) [TOC] Unity在3.0之后,支持基于约定的自动注册机制Registration By Convention,本文简单介绍如何配置. 1. 通过Nuget下载Unity 版本号如下: ...
- 微软IOC容器Unity简单代码示例2-配置文件方式
@(编程) 1. 通过Nuget下载Unity 这个就不介绍了 2. 接口代码 namespace UnityDemo { interface ILogIn { void Login(); } } n ...
- 微软IOC容器Unity简单代码示例1
@(编程) 1. 通过Nuget下载Unity 这个就不介绍了 2. 接口代码 namespace UnityDemo { interface ILogIn { void Login(); } } 3 ...
随机推荐
- 【原创】纯干货,Spring-data-jpa详解,全方位介绍。(转)
本篇进行Spring-data-jpa的介绍,几乎涵盖该框架的所有方面,在日常的开发当中,基本上能满足所有需求.这里不讲解JPA和Spring-data-jpa单独使用,所有的内容都是在和Spring ...
- jenkins执行shell命令提示找不到命令解决办法
用jenkins执行shell脚本,执行一条命令: #唤醒休眠手机 adb shell input keyevent 提示: [adb] $ /bin/sh -xe /Users/xxxxx/tool ...
- win7重命名文件时 提示 “指定的设备名无效”的解决办法
同事从mac上传一个文件夹到win7上,但是少了一张图片con.jpg.查了半天发现将备份文件改名为con.jpg时提示 “指定的设备名无效”. 谷歌了下,发现了问题所在.坑爹的win7. 从不同的系 ...
- django -- 为model 指定数据库名
一.为model指定数据库名: django自己实现的ORM中.如果要指定一个model的表名是通过Meta类来实现的. from django.db import models # Create y ...
- python sort和sorted函数
sort 与 sorted 区别: sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作. list 的 sort 方法返回的是对已经存在的列表进行操作,而内建函数 ...
- tmux用于恢复远程屏幕
1.我主要用tmux在远程登陆后,恢复以前会话时候用. 2.tmux创建新会话: tmux new -s 会话名 3.返回控制台: Ctrl+b d ,Ctrl+b命令是tmux前置命令,每次都要先输 ...
- C#实现RSA加密与解密、签名与认证
一.RSA简介 RSA公钥加密算法是1977年由Ron Rivest.Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的.RSA取名来自开发他们三者的名字.RSA是目前最有影响力 ...
- zabbix 实现 iptables 监控
安装iptstate # yum install iptstate 配置zabbix key iptables.conf # cat /etc/zabbix/zabbix_agentd.d/iptab ...
- C++类中的成员函数和构造函数为模板函数时的调用方法
所谓模板函数其实就是建立一个通用函数,这个通用函数的形参类型不具体指定,用一个虚拟类型来代表,这个通用函数就被称为函数模板. 例: #include <iostream> using na ...
- lua -- 在面板中添加多个部件
function UIBagController:initItemView( ) -- 获取面板 self.panelCenter = tolua.cast(UIHelper:seekWidgetBy ...