标题: 依赖注入容器Unity Application Block(2):Unity的春天
- overred 2008-03-04 23:54 阅读:2760
- 评论:12 查看评论 | 添加评论

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

其实.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
    希望本文能您对有所帮助! 

本文示例代码下载
查看评论 | 添加评论
返回顶部 | 返回首页