Unity Application Block 1.2 学习笔记

昨天花了一天时间,把IOC/DI的相关文章以及Unity相关的一些文章基本在园子里搜了个遍

先给出几篇不错的文章链接:

Unity Application Block官方网址 http://www.codeplex.com/unity

吕震宇整理的[Object Builder Application Block] http://www.cnblogs.com/zhenyulu/articles/641728.html

吕震宇[你真的了解Ioc与AOP吗?] http://www.cnblogs.com/zhenyulu/articles/233966.html

坚强2002翻译的[Inversion of Control Containers and the Dependency Injection pattern] http://www.cnblogs.com/me-sa/archive/2008/07/30/IocDI.html

赤脚小子的[unity系列] http://www.cnblogs.com/kebixisimba/archive/2008/05/19/1202467.html
 
NEE's [Unity 配置:typeConverter的使用] http://www.cnblogs.com/nickyao/archive/2008/05/04/1181804.html

Warmth & Chilliness的[Unity -- .NET下的原生Ioc框架,让一部分人先用起来] http://www.cnblogs.com/think8848/archive/2008/10/25/1319616.html

基本上把上面的这些个文章全耐着性子看完,相关知识点的“扫盲”工作也差不多完成了

这里只是把我练习的一个例子贴在这里,并发表一些个人粗浅的看法

应用场景:随便给一些数字,要求对这些数字进行一项基本的数据运算(例子中只实现了加法/乘法)

先添加对Microsoft.Practices.Unity.dll的引用

准备测试用的接口和类:

/// <summary>
    /// 接口
    /// </summary>
    public interface Icalculate
    {
        int Calculate(params int[] a);

        double Calculate(params double[] a);

        string GetOperationName();

        
    }

    /// <summary>
    /// 加法运算
    /// </summary>
    public class Addtive : Icalculate
    {
        /// <summary>
        /// 注意:至少要有一个构造函数,否则用配置文件方式Resolve<Icalculate>时会提示:Icalculate是一个接口,没有构造函数,所以不能创建实例云云,但有趣的是用硬编码方式却可以成功
        /// </summary>
        public Addtive() { }

        public int Calculate(params int[] a)
        {
            int Result = 0;

            foreach (int x in a)
            {
                Result += x;
            }

            return Result;
        }

        public double Calculate(params double[] a)
        {
            double Result = 0.0;

            foreach (double x in a)
            {
                Result += x;
            }

            return Result;
        }

        public string GetOperationName()
        {
            return "加法";
        }
    }

    /// <summary>
    /// 乘法运算
    /// </summary>
    public class Multiplication : Icalculate
    {

        public Multiplication() { }

        public int Calculate(params int[] a)
        {
            int Result = 1;

            foreach (int x in a)
            {
                Result *= x;
            }

            return Result;
        }

        public double Calculate(params double[] a)
        {
            double Result = 1.0;

            foreach (double x in a)
            {
                Result *= x;
            }

            return Result;
        }

        public string GetOperationName()
        {
            return "乘法";
        }
    }

    /// <summary>
    /// (四则)运算管理器
    /// </summary>
    public class CalcManager
    {
        private Icalculate _calc;

        public CalcManager(Icalculate IC)
        {
            _calc = IC;
        }

        //[InjectionMethod]
        //public void SetCalculate(Icalculate IC) {
        //    _calc = IC;
        //}

        public void Compute(params int[] a)
        {
            string _paramName = "";

            foreach (int x in a)
            {
                _paramName += x.ToString() + ",";
            }
            _paramName = _paramName.Trim(',');

            Console.WriteLine("{0} {1}计算结果:{2}", _paramName, _calc.GetOperationName(), _calc.Calculate(a));
        }

        public void Compute(params double[] a)
        {
            string _paramName = "";

            foreach (double x in a)
            {
                _paramName += x.ToString() + ",";
            }
            _paramName = _paramName.Trim(',');

            Console.WriteLine("{0} {1}计算结果:{2}", _paramName, _calc.GetOperationName(), _calc.Calculate(a));
        }

    }

为了对比,我们先用传统方式来调用试下:

static void Main(string[] args)
{
    #region 不用依赖注入的传统方式
    CalcManager CM = new CalcManager(new Addtive());
    CM.Compute(1, 2, 3, 4, 5);//计算1,2,3,4,5的和

    //CM = new CalcManager(new Multiplication());
    //CM.Compute(1, 2, 3, 4, 5);//计算1,2,3,4,5的乘积
    #endregion            

    Console.ReadLine();
}

虽然简单易懂,但细想一下可扩展性并不高,如果以后又增加了除法,平方,减法...等一系列算法,不是每次都要这一段代码吗?原因就是接口,算法实体类,调用程序之间的耦合性太高

接下来,我们用Unity换一种写法:

using System;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using System.Configuration;

static void Main(string[] args)
{
    

    #region 使用依赖注入(硬编码方式)
    IUnityContainer container = new UnityContainer();
    container.RegisterType<Icalculate, Addtive>() //注入加法类
         .RegisterType<CalcManager>();//注入管理器            

    CalcManager CM = container.Resolve<CalcManager>();//取得CalcManager的实例
    CM.Compute(1.1, 2.9, 3.1, 4, 5);

    //container.RegisterType<Icalculate, Multiplication>();//继续注入乘法类

    //CM = container.Resolve<CalcManager>();
    //CM.Compute(1, 2, 3, 4, 5);
    #endregion

    

    Console.ReadLine();
}

单从代码上看,只不过换了种写法和思路,但仍然属于“硬编码”的方式,如果要增加其它算法或换成其它算法,一样还是要改这段代码.(貌似纯属瞎折腾?呵呵)

下面切入正题,Unity除了这种硬编码方式,还允许把注入规则/映射写到配置文件里

先修改App.Config,内容大致如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration, Version=1.2.0.0, Culture=neutral" />
  </configSections>
  <unity configSource="config\DI.config" />
</configuration>

同时再新建一个config目录,把DI.config文件放在该目录下,内容:

<?xml version="1.0" ?>
<unity>
  <typeAliases>
    <typeAlias alias="ICalc" type="UnityStudy.Icalculate, UnityStudy" />
    <typeAlias alias="Add" type="UnityStudy.Addtive, UnityStudy" />
    <typeAlias alias="Mul" type="UnityStudy.Multiplication, UnityStudy" />
    <typeAlias alias="CM" type="UnityStudy.CalcManager, UnityStudy" />
  </typeAliases>
  <containers>
    <container>
      <types>
        <type type="ICalc" mapTo="Mul"/>      
        <!--结实验,下面这一行加不加程序都能运行,只要确保CalcManager中有一个参数为Icalculate的构架函数或(注入)设置方法就行[参看CalcManager中注释掉的部分],Unity在这一点上确实比较“智能”-->
        <type type="CM"/>       
      </types>
    </container>
  </containers>
</unity>

调用代码再换一种写法:

static void Main(string[] args)
{
    
    #region 使用依赖注入(配置文件方式)
    IUnityContainer container = new UnityContainer();
    UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
    section.Containers.Default.Configure(container);

    CalcManager CM = container.Resolve<CalcManager>();
    CM.Compute(1, 2, 3, 4, 5);
    #endregion

    Console.ReadLine();
}

这回仔细看下,代码中已经完全找不到Addtive,Multiplication等这些具体的类名了,整个程序完全依赖于配置文件中的相关节点(其实OO的角度来讲,是基于接口Icalculate的编程以及不同对象的组合让这一切成为可能)。

如果我们要把乘法运算换成加法运算,太容易了,把DI.config中的

<type type="ICalc" mapTo="Mul"/>   

换成

<type type="ICalc" mapTo="Add"/>

原来的调用代码一行都不用改!

最后:Unity除了实现IOC/DI之外还有一些其它用途,比如:实现单件模式(而且这种实现方式更灵活,比如我们可以让任何一个普通的类,在容器的生命周期内仅返回一个实例,这是传统的单件模式中"把类硬编码定死为单件实例"无法做到的)

时间: 2025-01-24 08:52:29

Unity Application Block 1.2 学习笔记的相关文章

Unity Application Block 1.0系列(1):快速开始

Unity Application Block (Unity) 是微软 Patterns & Practices 团队开发的一个轻量级.可扩展的依赖注入容器,有助于构建松散耦合的系统.它支持构造子注入 (Constructor Injection) , 属性/设值方法注入 (Property/Setter Injection) 和方法调用注入 (Method Call Injection) .Patterns & Practices 团队于前些天(4月4日)发布了Unity的第一个正式版本

Unity Application Block 1.0系列(7):Lifetime Managers

Unity使用继承于 LifetimeManager 基类的类去控制怎样存放到对象实例的引用和容器怎样销毁这些实例,也就是说Unity基于具体的Lifetime Manager 类去管理对象的创建和销毁. 目前Unity中提供两个Lifetime Manager类可供我们直接使用,当然你也可以实现自己的Lifetime Manager类. 1. ContainerControlledLifetimeManager Unity保存一个指向对象实例的引用.通过Unity容器为同一个类型或对象获取对象

Unity Application Block 1.0系列(6):杜绝循环引用

在介绍 Constructor Injection.Property Injection和 Method Call Injection时,都有特别提到不要出现循环引用(Circular References),因为出现这种问题后很难去检测到.最好的解决方法是写代码时候尽量避免出现这种情况. 下面看看循环引用的几个场景,在使用依赖注入框架时应该尽量避免出现这样的情况: 1.通过Constructor Injection生成的对象在构造器的参数中互相引用 public class Class1{ p

Unity Application Block 1.0系列(5): 使用BuildUp让已存在对象实例也支持依

通过Unity容器的BuildUp方法可以让已存在的对象实例也可以支持依赖注入. 准备工作 先看一些类,下面会用到: public class Owner{ public string Name { get { return "Inrie"; } } public int Age { get { return 24; } }}public abstract class Player{ public abstract void Play(); public abstract string

自定义Unity 容器的扩展 --- Unity Application Block Event Broker

Unity快速入门的例子中有一个Block Event Broker通过对容器功能的扩展来实现事件截获,其中扩展的代码在项目EventBrokerExtension.该项目允许在应用程序种使用自定义的容器扩展来发布和订阅事件. 自定义Unity 容器扩展是一个从 UnityContainerExtension 基类继承而来并且实现了一些额外的方法的自定义类型.该扩展可以访问容器,并且可以接收到容器中已经注册的事件. SimpleEventBrokerExtension 类继承 UnityCont

使用Unity Application Block(三):理解和使用依赖注入的键

在使用 Unity 创建对象之前,需要准备 Unity 容器,也即进行注册(类型映射).在 使用Unity(二):配置 Unity .读取配置信息和获取对象中, 我们学习了如何使用配置文件来进行注册,而在本文中,我们将学习使用代码来进行各种注册,这些代码所实现的功能同样可以使用配置文件来实现. 因为 Unity 自带的文档已在这方面进行了详细说明,所以在此仅对该文档进行了翻译,以下是译文. Unity 应用程序块容器用键以及可选的名称来标识注册(类型映射).键是接口一个接口或者一个类(通常是基类

Unity Application Block 1.0系列(4):方法调用注入(Method Call Injection)

什么情况下使用Method CallInjection 当实例化父对象时也能自动实例化所依赖的对象 通过简单的方式使得很容易做到在代码中查看每个类所依赖的项 父对象有很多相互之间有关联关系的构造器,导致在调试和维护时很不方便 父对象包含有很多参数构造器,特别是参数类型相似的只能通过参数的位置来辨别的 隐藏依赖的对象,不作为属性暴露出去 通过修改依赖对象的代码来控制哪些对象可以被注入,而不用改动父对象或应用程序 准备工作 public interface IPlayer { void Play()

Unity Application Block 1.0系列(3):属性/设值方法注入(Property/Setter Inj

什么情况下使用Property (Setter) Injection 当实例化父对象时也能自动实例化所依赖的对象 通过简单的方式使得很容易做到在代码中查看每个类所依赖的项 父对象有很多相互之间有关联关系的构造器,导致在调试和维护时很不方便. 父对象包含有很多参数构造器,特别是参数类型相似的只能通过参数的位置来辨别的 让用户(将调用这些代码的程序)更方便的看到有哪些对象可以用,这在Constructor Injection里是没办法实现的. 通过修改依赖对象的代码来控制哪些对象可以被注入,而不用改

Unity Application Block 1.0系列(2):构造子注入(Constructor Injection)

什么情况下使用Constructor Injection 当实例化父对象时也能自动实例化所依赖的对象 通过简单的方式使得很容易做到在代码中查看每个类所依赖的项 父对象的构造器不需要很多相互间有关联关系的构造器 父对象的构造器不需要很多参数 通过不使用属性和方法暴露给调用程序,达到封装字段值以不能被看到的目的 通过修改依赖对象的代码来控制哪些对象可以被注入,而不用改动父对象或应用程序 准备工作 public class Song { public string Singer { get { ret