依赖注入容器Unity Application Block(2):Unity的春天

春天的傍晚,太阳下去了,月亮还没有出,只剩下一片乌蓝的天和几绺徐徐的风。
希望这个开篇蜚语能给您带来春天的感觉和乍爽!

其实.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>();
我们把1925行修改为如下:
 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
    希望本文能您对有所帮助! 

本文示例代码下载
Tag标签: Unity,IOC
posted @ 2008-03-04 23:54 overred 阅读(2489) 评论(12)  编辑 收藏 所属分类: ① NET Framework

  回复  引用  查看    
#1楼 2008-03-05 08:29 | TerryLee      
呵呵,支持!
  回复  引用  查看    
#2楼 2008-03-05 08:29 | 高海东      
不错 多来点这个方面的
  回复  引用    
#3楼 2008-03-05 08:37 | 牛牛牛 [未注册用户]
如果把一个.net程序员拘留几个月,那么当他越狱出来的时候,在某个技术方面也许会被我们落下整整一个世纪!!

大实话
  回复  引用  查看    
#4楼 2008-03-05 09:09 | 早班火车~      
开始学习学习Enterprise Library 4.0~
其实一直都用2.0哈哈~
  回复  引用  查看    
#5楼 2008-03-05 09:20 | TerryLee      
@早班火车~
在本月15号微软将发布Enterprise Library 4.0第一个CTP版本:)
  回复  引用  查看    
#6楼 2008-03-05 10:51 | 江大鱼      
Enterprise Library 4.0 will be released at mid-end of April
  回复  引用  查看    
#7楼 2008-03-05 11:26 | 李华星      
作者挺幽默,内容还来不及看, 收藏一下先
  回复  引用  查看    
#8楼 [楼主]2008-03-05 12:11 | overred      
@TerryLee
@高海东
@牛牛牛
@早班火车~
@江大鱼
@李华星
^+^
  回复  引用  查看    
#9楼 2008-03-06 00:42 | 浪子      
楼主文章的主题组织有点乱额,看了有点迷糊:)
  回复  引用  查看    
#10楼 [楼主]2008-03-06 20:12 | overred      
@浪子
谢谢
希望形散而神不散
  回复  引用  查看    
#11楼 2008-03-24 17:21 | Tony Zhou      
"而Log.cs修为为如下代码即可:"
写错了吧,应该是Program.cs吧
  回复  引用  查看    
#12楼 [楼主]2008-03-25 22:50 | overred      
@Tony Zhou
谢谢斧正
已修改

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-03-25 22:45 编辑过
 

另存  打印