春天的傍晚,太阳下去了,月亮还没有出,只剩下一片乌蓝的天和几绺徐徐的风。
希望这个开篇蜚语能给您带来春天的感觉和乍爽!
其实.net的春天也来了:Enterprise Library 4.0来了;MVC来了;Unity来了;他老爹Scott Guthrie特洛夫斯基也跟着来了。
“如果把一个.net程序员拘留几个月,那么当他越狱出来的时候,在某个技术方面也许会被我们落下整整一个世纪!!”
OK,那就让我们体会浸染这“二重春”的快感吧!
别找了先,这个“依赖注入容器Unity Application Block(2):”是吸取日月精华从石缝里蹦出来的,我不老歌上是没有“依赖注入容器Unity Application Block(1)”的,那就让我这个说书的人,用充满乡音的口吻,跳过水坑绕过小村,开始正文:
资源:
Enterprise Library 4.0中的依赖注入容器(Unity)预览
依赖注入容器Unity Application Block(1):快速入门
(来自Terrylee博客)
有(1)故有此(2)。
一:本文要点:
1.使用Unity如何实现Castle里的自动装配(IOC)
2.使用Unity需要注意那些
二:Unity实现自动装配(Don't call me,I will call u)
在开始这场战争前,建议大家先看看这个:Castle IOC容器快速入门 (来自Terrylee博客),也算作一份战前通告吧,我的示例代码也尽量形近于她!
示例场景:记录日志的时候我们采用不同模板,来展现不同的日志风格
代码准备:
1.两个接口代码
ILog.cs
/*
* 描述:日志接口:把“写日志”这个行为抽象出来
* 功能:
* overred 2008/03/04 惊蛰前
* http://overred.cnblogs.com
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityIOCDemo
{
interface ILog
{
void Write(string msgStr);
}
}
ILogFormatter.cs
/*
* 描述:日志模板接口
* 功能:
* overred 2008/03/04 惊蛰前
* http://overred.cnblogs.com
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityIOCDemo
{
interface ILogFormatter
{
string Format(string msgStr);
}
}
2.接口实现类:
(1)写日志类(Log.cs):
/*
* 描述:写日志
* 功能:可以使用模板格式写入日志
* overred 2008/03/04 惊蛰前
* http://overred.cnblogs.com
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityIOCDemo
{
class Log:ILog
{
private ILogFormatter _format;
public Log(ILogFormatter format)
{
this._format = format;
}
/// <summary>
/// ILog接口方法实现
/// </summary>
/// <param name="msgStr"></param>
public void Write(string msgStr)
{
string str = _format.Format(msgStr);
Console.WriteLine(str);
}
}
}
(2)日志模板类(LogFormatter.cs)
/*
* 描述:日志模板类
* 功能:
* overred 2008/03/04 惊蛰前
* http://overred.cnblogs.com
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityIOCDemo
{
class LogFormatter:ILogFormatter
{
public string Format(string msgStr)
{
return string.Format("LogFormatter:[{0}]",msgStr);
}
}
}
3.创建容器、注册接口并映射方法实现控制反转(Program.cs)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 using Microsoft.Practices.Unity;
7
8 namespace UnityIOCDemo
9 {
10 class Program
11 {
12 static void Main(string[] args)
13 {
14 //创建容器
15 IUnityContainer container = new UnityContainer();
16
17 //注册接口并映射到实现方法
18 container.Register<ILogFormatter, LogFormatter>();
19 container.Register<ILog, Log>();
20
21 //获取组件
22 ILog log = container.Get<ILog>();
23
24 //调用写日志方法
25 log.Write("overred");
26 }
27 }
28 }
29
运行结果:
简单的解释一下:
第一步先创建一个容器container;
第二步注册接口并实现接口映射:ILogFormatter-->LogFormatter;ILog-->Log
第三步获取组件
第四步调用方法
总结:
我们在Log中调用了接口ILogFormatter,而ILogFormatter又依赖于LogFormatter,这就是Unity很黄很暴力的一个地方:
她能够自动探测组件间的依赖关系,从而实现自动装配!!
下面说明一下以上应该注意的地方:
1.在Program.cs代码片段中:行18和行19没有先后关系,因为他们是不同的接口和类;
2.container.Register<ILog, Log>();
原型:IUnityContainer Register<TFrom, TTo>() where TTo : TFrom;
TTo 受约束于TFrom,即Log受约束于ILog,但是我们可以container.Register<Log, Log>();
我们把19至25行修改为如下:
container.Register<Log, Log>();
//获取组件
ILog log = container.Get<Log>();
//调用写日志方法
log.Write("overred");
输出结果是一样的!
3.在实现自动装配的时候Program.cs代码片段中第18行的
container.Register<ILogFormatter, LogFormatter>();
不能重载为:
container.Register<ILogFormatter, LogFormatter>("LogFormatter");
否则会报异常(不知道是不是bug,没仔细研究)
4.在实现注册映射时构造函数public Log(ILogFormatter format)参数不能为string,int等值类型,需要为接口或者类
5.需要自动装载的接口或者类需要放在被调用者的构造函数中:如public Log(ILogFormatter format),这也许是她们肌肤之触最近的一次!
6.其他的还没发现,以上几个我个人观点难免有不妥之处,希望你指出,谢谢先!
三 扩展
1.如果我的Log构造函数中有多个接口或者类作为参数的时候该如何办?
当为多个接口的情况下:
比如新增一个接口(ILogFormatter2.cs)
/*
* 描述:日志模板接口2
* 功能:
* overred 2008/03/04 惊蛰前
* http://overred.cnblogs.com
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityIOCDemo
{
interface ILogFormatter2
{
string Format(string msgStr);
}
}
新增一个实现方法(LogFormatter2.cs)
/*
* 描述:日志模板类2
* 功能:
* overred 2008/03/04 惊蛰前
* http://overred.cnblogs.com
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityIOCDemo
{
class LogFormatter2 : ILogFormatter2
{
public string Format(string msgStr)
{
return string.Format("LogFormatter2:[{0}]", msgStr);
}
}
}
而Program.cs修为为如下代码即可:
1 static void Main(string[] args)
2 {
3 //创建容器
4 IUnityContainer container = new UnityContainer();
5
6 //注册接口并映射到实现方法
7 container.Register<ILogFormatter, LogFormatter>();
8 container.Register<ILogFormatter2, LogFormatter2>();//新增模板2
9 container.Register<ILog, Log>();
10
11 //获取组件
12 ILog log = container.Get<ILog>();
13
14
15 //调用写日志方法
16 log.Write("overred");
17 }
这样输出结果为:

说明:Unity杀一而敬百,不管多少个接口,只要你注册并映射整个世界就一片和谐!!
如果参数是类的情况:
我们新增一个类(MyClass.cs),此类有一个返回字符串的方法
/*
* 描述:
* 功能:
* overred 2008/03/04 惊蛰前
* http://overred.cnblogs.com
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityIOCDemo
{
class MyClass
{
public string GetString()
{
return "this is MyClass";
}
}
}
然后让Log的构造函数参数之一是:MyClass这个类
1 class Log:ILog
2 {
3 private ILogFormatter _format;
4 private ILogFormatter2 _format2;//新增模板接口2
5 private MyClass _myclass;//新增类
6
7
8 public Log(ILogFormatter format,ILogFormatter2 format2,MyClass myclass)
9 {
10 this._format = format;
11 this._format2 = format2;//
12 this._myclass = myclass;//
13 }
14
15 /// <summary>
16 /// ILog接口方法实现
17 /// </summary>
18 /// <param name="msgStr"></param>
19 public void Write(string msgStr)
20 {
21 string str = _format.Format(msgStr) + _format2.Format(msgStr) + _myclass.GetString();//调用GetString()方法
22 Console.WriteLine(str);
23 }
24 }
也许你会说我们接下来要做的是在Program.cs里增加:
container.Register<MyClass,MyClass>();
其实已经没必要了,我们只要在Program.cs里增加如下代码:
MyClass mc = container.Get<MyClass>();
Console.WriteLine(mc.GetString());
输出结果:

从结果中可以看到我们在容器中可以获取MyClass,她已经躺在容器的沙发上,等着你!
所以那个
Log的构造函数是他们藕不断丝还连,缠绵的温床!
2.如果一个接口有多个实现,我同时注册并映射,结果会怎样?
在castle里的结论为:
关于Castle IOC容器自动装配的问题
TerryLee大哥的解释为:
如果有多个类(组件)实现同一个接口(服务),容器会自动选择最先加入到容器中的组件来装配。对于这样的结果,其实我们并不感觉到意外,每次注册组件时,容器都会检测它的依赖性,当加入第一个ILogFormatter的组件时,容器检测到TextFileLog已经满足了它的依赖性,所以它不会再去装配第二个
而Unity里正好跟她相反,它装配最后一个满足她的依赖!!!
3.如果我有多个构造函数,Unity又怎知道我注册和映射的是那个?
估计这样她就会害羞的低下头,在Unity里有一些特性如构造函数的是[InjectionConstructor],让你知道他究竟想掀起哪个美女的盖头:
1 public Log(ILogFormatter format)
2 {
3 this._format = format;
4
5 }
6
7 [InjectionConstructor]
8 public Log(ILogFormatter2 format2)
9 {
10 this._format2 = format2;
11 }
注意:第
7行的特性不能加在第
1行之上,否则会报错为:你有多个构造函数,所以是靠下原则!!
四 总结
这两天家里上网不便,故研究Unity以解闷。暂得出以上经验与大家分享,Unity新版本马上开张造势!!!
你有暂住证吗?拘留你10天,我们就领先你半个世纪!!!
五 参考资源
http://www.codeplex.com/unity
http://terrylee.cnblogs.com
希望本文能您对有所帮助!
本文示例代码下载