Unity 依赖注入

时间:2022-04-24
本文章向大家介绍Unity 依赖注入,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

关于Ioc的框架有很多,比如astle Windsor、Unity、Spring.NET、StructureMap,我们这边使用微软提供的Unity做示例,你可以使用Nuget添加Unity,也可以引用Microsoft.Practices.Unity.dll和Microsoft.Practices.Unity.Configuration.dll,下面我们就一步一步的学习下Unity依赖注入的详细使用。如果不明白什么是控制反转和依赖注入,请参考控制反转和依赖注入模式

下面通过一个示例来讲解Unity不同的依赖注入,现在有一家公司,这家公司有很多的员工,这些员工分别来自不同的省份,有的是浙江人,有的是四川人,也有的是湖南人等等,因为公司上了一定的规模,所以为了解决员工的吃饭问题,所以公司决定built一个食堂,但是不同地方的员工的口味不同,所以食堂必须具备烹饪不同菜系的功能,ok,接下来就围绕这这个例子来讲解Unity的依赖注入。

1、构造器注入

IOC容器会智能的选择和调用合适的构造函数,以创建依赖的对象,如果被选择的构造函数具有相应的参数,IOC容器在调用构造函数之前会解析注册的依赖关系并自行获得相应的参数。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Practices.Unity;

namespace DependencyInto
{
    class Program
    {
        static void Main(string[] args)
        {
            UnityContainer contanier = new UnityContainer();
            //向IOC容器中注册浙江系菜和四川系菜
            contanier.RegisterType<IMess, SiChuanFood>();
            contanier.RegisterType<IMess, ZhengJiangFood>();

            //IOC容器会智能的选择和调用合适的构造函数,以创建依赖的对象,如果被选择的构造函数具有相应的参数,IOC容器在调用构造函数之前会解析注册的依赖关系并自行获得相应的参数。
            //获取可行的参数后,将参数注入到对应的类中
            IEmployee employee=contanier.Resolve<SiChuanEmployee>();
            IEmployee employees = contanier.Resolve<ZheJiangEmployee>();
            employee.EatFood();
            employees.EatFood();
            Console.ReadLine();
        }
    }
    /// <summary>
    /// 员工接口,里面包含员工最基本的权利
    /// </summary>
    internal interface IEmployee
    {
        void EatFood();
    }

    /// <summary>
    /// 食堂接口,里面包含食堂最基本的用途
    /// </summary>
    internal interface IMess
    {
        string GetFood();
    }

    /// <summary>
    /// 浙江系菜
    /// </summary>
    internal class ZhengJiangFood : IMess
    {

        public string GetFood()
        {
            return "浙江菜";
        }
    }

    /// <summary>
    /// 四川系菜
    /// </summary>
    internal class SiChuanFood : IMess
    {

        public string GetFood()
        {
            return "四川菜";
        }
    }


    /// <summary>
    /// 四川员工
    /// </summary>
    internal class SiChuanEmployee : IEmployee
    {
        private IMess _mess; 

        /// <summary>
        /// 通过构造函数注入食堂接口实例
        /// </summary>
        /// <param name="mess">食堂接口实例</param>
        public SiChuanEmployee(IMess mess)
        {
            this._mess = mess;
        }
        public void EatFood()
        {
            Console.WriteLine("四川人吃" + _mess.GetFood());
        }
    }

    /// <summary>
    /// 浙江员工
    /// </summary>
    internal class ZheJiangEmployee : IEmployee
    {
        private IMess _mess;
        /// <summary>
        /// 通过构造函数注入食堂接口实例
        /// </summary>
        /// <param name="mess">食堂接口实例</param>
        public ZheJiangEmployee(IMess mess)
        {
            this._mess = mess;
        }
        public void EatFood()
        {
            Console.WriteLine("浙江人吃"+_mess.GetFood());
        }
    }
}

UnityContainer的实例方法:RegisterType   向容器中注册需要通过容器生成的对象

UnityContainer的实例方法:Resolve  设置生成的对象的注入目标(就是设置生成的对象需要注入哪个目标)

2、属性注入-通过Dependency特性

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Practices.Unity;

namespace DependencyInto
{
    class Program
    {
        static void Main(string[] args)
        {
            UnityContainer contanier = new UnityContainer();
            //向IOC容器中注册浙江系菜和四川系菜
            contanier.RegisterType<IMess, SiChuanFood>();
            contanier.RegisterType<IMess, ZheJiangFood>();

            //IOC容器会智能的选择和调用合适的构造函数,以创建依赖的对象,如果被选择的构造函数具有相应的参数,IOC容器在调用构造函数之前会解析注册的依赖关系并自行获得相应的参数。
            //获取可行的参数后,将参数注入到对应的类中
            IEmployee employee=contanier.Resolve<SiChuanEmployee>();
            IEmployee employees = contanier.Resolve<ZheJiangEmployee>();
            employee.EatFood();
            employees.EatFood();
            Console.ReadLine();
        }
    }
    /// <summary>
    /// 员工接口,里面包含员工最基本的权利
    /// </summary>
    internal interface IEmployee
    {
        void EatFood();
    }

    /// <summary>
    /// 食堂接口,里面包含食堂最基本的用途
    /// </summary>
    internal interface IMess
    {
        string GetFood();
    }

    /// <summary>
    /// 浙江系菜
    /// </summary>
    internal class ZheJiangFood : IMess
    {

        public string GetFood()
        {
            return "浙江菜";
        }
    }

    /// <summary>
    /// 四川系菜
    /// </summary>
    internal class SiChuanFood : IMess
    {

        public string GetFood()
        {
            return "四川菜";
        }
    }


    /// <summary>
    /// 四川员工
    /// </summary>
    internal class SiChuanEmployee : IEmployee
    {

        #region 属性注入依赖
        private IMess _mess;

        [Dependency]
        public IMess Mess
        {
            get
            {
                return this._mess;
            }
            set
            {
                this._mess = value;
            }
        }
        #endregion

        public void EatFood()
        {
            Console.WriteLine("四川人吃" + Mess.GetFood());
        }
    }

    /// <summary>
    /// 浙江员工
    /// </summary>
    internal class ZheJiangEmployee : IEmployee
    {

        #region 属性注入依赖
        private IMess _mess;

        [Dependency]
        public IMess Mess
        {
            get
            {
                return this._mess;
            }
            set
            {
                this._mess = value;
            }
        } 
        #endregion

        public void EatFood()
        {
            Console.WriteLine("浙江人吃"+_mess.GetFood());
        }
    }
}

ok,输出结果一样,通过Dependency特性声明需要外部注入依赖的属性,注:该特性

3、方法注入-通过InjectionMethod特性

方法注入和属性方式使用一样,方法注入只需要在方法前加[InjectionMethod]标记就行了从方法注入的定义上看,只是模糊的说对某个方法注入,但是方法注入无非三种:

a、方法参数注入

b、方法返回值注入

c、方法中的引用注入

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Practices.Unity;

namespace DependencyInto
{
    class Program
    {
        static void Main(string[] args)
        {
            UnityContainer contanier = new UnityContainer();
            //向IOC容器中注册浙江系菜和四川系菜
            contanier.RegisterType<IMess, SiChuanFood>();
            contanier.RegisterType<IMess, ZheJiangFood>();

            //IOC容器会智能的选择和调用合适的构造函数,以创建依赖的对象,如果被选择的构造函数具有相应的参数,IOC容器在调用构造函数之前会解析注册的依赖关系并自行获得相应的参数。
            //获取可行的参数后,将参数注入到对应的类中
            IEmployee employee = contanier.Resolve<SiChuanEmployee>();
            ZheJiangEmployee employees = contanier.Resolve<ZheJiangEmployee>();
            employee.EatFood();
            employees.EatFood();
            Console.WriteLine("people.tool == null(引用) ? {0}", employees._mess1== null ? "Yes" : "No");
            Console.WriteLine("people.tool2 == null(参数) ? {0}", employees._mess2 == null ? "Yes" : "No");
            Console.WriteLine("people.tool3 == null(返回值) ? {0}", employees._mess3 == null ? "Yes" : "No");
            Console.ReadLine();
        }
    }
    /// <summary>
    /// 员工接口,里面包含员工最基本的权利
    /// </summary>
    internal interface IEmployee
    {
        void EatFood();
    }

    /// <summary>
    /// 食堂接口,里面包含食堂最基本的用途
    /// </summary>
    internal interface IMess
    {
        string GetFood();
    }

    /// <summary>
    /// 浙江系菜
    /// </summary>
    internal class ZheJiangFood : IMess
    {

        public string GetFood()
        {
            return "浙江菜";
        }
    }

    /// <summary>
    /// 四川系菜
    /// </summary>
    internal class SiChuanFood : IMess
    {

        public string GetFood()
        {
            return "四川菜";
        }
    }


    /// <summary>
    /// 四川员工
    /// </summary>
    internal class SiChuanEmployee : IEmployee
    {

        #region 方法注入
        public IMess _mess1;//我是对象引用
        public IMess _mess2;//我是参数
        public IMess _mess3;//我是返回值

        /// <summary>
        /// 通过方法里面的引用注入
        /// </summary>
        [InjectionMethod]
        public void MethodInto1()
        {
            if (object.ReferenceEquals(_mess1, null)) { }
        }

        /// <summary>
        /// 通过方法参数注入
        /// </summary>
        /// <param name="mess"></param>
        [InjectionMethod]
        public void MethodInto2(IMess mess)
        {
            this._mess2 = mess;
        }

        /// <summary>
        /// 通过方法返回值注入
        /// </summary>
        /// <param name="mess"></param>
        [InjectionMethod]
        public IMess MethodInto3()
        {
            return _mess3;
        }

        #endregion

        public void EatFood()
        {
            Console.WriteLine("四川人吃" + _mess2.GetFood());
        }
    }

    /// <summary>
    /// 浙江员工
    /// </summary>
    internal class ZheJiangEmployee : IEmployee
    {

        #region 方法注入
        public IMess _mess1;//我是对象引用
        public IMess _mess2;//我是参数
        public IMess _mess3;//我是返回值

        /// <summary>
        /// 通过方法里面的引用注入
        /// </summary>
        [InjectionMethod]
        public void MethodInto1()
        {
            if (object.ReferenceEquals(_mess1, null)) { }
        }

        /// <summary>
        /// 通过方法参数注入
        /// </summary>
        /// <param name="mess"></param>
        [InjectionMethod]
        public void MethodInto2(IMess mess)
        {
            this._mess2 = mess;
        }

        /// <summary>
        /// 通过方法返回值注入
        /// </summary>
        /// <param name="mess"></param>
        [InjectionMethod]
        public IMess MethodInto3()
        {
            return _mess3;
        }

        #endregion

        public void EatFood()
        {
            Console.WriteLine("浙江人吃" + _mess2.GetFood());
        }
    }
}

4、配置文件配置IOC

到目前位置三种依赖注入的三种方式,都已近介绍了,但是除了构造器注入当我们使用属性注入和方法注入的时候,并通过RegisterType,会产生代码产生耦合,当我们添加一个方法或者一个属性或者添加一个方法,都需要去修改代码,这中设计显然是不太合理的,所以我们要做的是,不去修改代码而是通过修改配置文件的方式,具体代码如下:

app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 
<configSections>
  <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration" />
  </configSections>
  <unity>
    <containers>
      <container name="defaultContainer">
        <register type="DependencyInto.IMess, DependencyInto" mapTo="DependencyInto.ZheJiangFood, DependencyInto"/>
        <register type="DependencyInto.IEmployee,DependencyInto" mapTo="DependencyInto.ZheJiangEmployee,DependencyInto"/>
      </container>
    </containers>
  </unity>
  
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>

指定自定义节点名称,和处理自定义节点的一般处理程序

unity配置节点

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

namespace DependencyInto
{
    class Program
    {
        static void Main(string[] args)
        {
            UnityContainer container = new UnityContainer();            
            //UnityConfigurationSection.SectionName="untiy"
            //通过GetSection()获得unity自定义节点,并且根据该节点下面的内容生成UnityConfigurationSection实例
            UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);
            configuration.Configure(container, "defaultContainer");//设置容器的名称,并对其进行配置
            IEmployee people = container.Resolve<IEmployee>();
            people.EatFood();
            Console.ReadLine();
        }
    }
    /// <summary>
    /// 员工接口,里面包含员工最基本的权利
    /// </summary>
    internal interface IEmployee
    {
        void EatFood();
    }

    /// <summary>
    /// 食堂接口,里面包含食堂最基本的用途
    /// </summary>
    internal interface IMess
    {
        string GetFood();
    }

    /// <summary>
    /// 浙江系菜
    /// </summary>
    internal class ZheJiangFood : IMess
    {

        public string GetFood()
        {
            return "浙江菜";
        }
    }

    /// <summary>
    /// 四川系菜
    /// </summary>
    internal class SiChuanFood : IMess
    {

        public string GetFood()
        {
            return "四川菜";
        }
    }


    /// <summary>
    /// 四川员工
    /// </summary>
    internal class SiChuanEmployee : IEmployee
    {

        #region 方法注入
        public IMess _mess1;//我是对象引用
        public IMess _mess2;//我是参数
        public IMess _mess3;//我是返回值

        /// <summary>
        /// 通过方法里面的引用注入
        /// </summary>
        [InjectionMethod]
        public void MethodInto1()
        {
            if (object.ReferenceEquals(_mess1, null)) { }
        }

        /// <summary>
        /// 通过方法参数注入
        /// </summary>
        /// <param name="mess"></param>
        [InjectionMethod]
        public void MethodInto2(IMess mess)
        {
            this._mess2 = mess;
        }

        /// <summary>
        /// 通过方法返回值注入
        /// </summary>
        /// <param name="mess"></param>
        [InjectionMethod]
        public IMess MethodInto3()
        {
            return _mess3;
        }

        #endregion

        public void EatFood()
        {
            Console.WriteLine("四川人吃" + _mess2.GetFood());
        }
    }

    /// <summary>
    /// 浙江员工
    /// </summary>
    internal class ZheJiangEmployee : IEmployee
    {

        #region 方法注入
        public IMess _mess1;//我是对象引用
        public IMess _mess2;//我是参数
        public IMess _mess3;//我是返回值

        /// <summary>
        /// 通过方法里面的引用注入
        /// </summary>
        [InjectionMethod]
        public void MethodInto1()
        {
            if (object.ReferenceEquals(_mess1, null)) { }
        }

        /// <summary>
        /// 通过方法参数注入
        /// </summary>
        /// <param name="mess"></param>
        [InjectionMethod]
        public void MethodInto2(IMess mess)
        {
            this._mess2 = mess;
        }

        /// <summary>
        /// 通过方法返回值注入
        /// </summary>
        /// <param name="mess"></param>
        [InjectionMethod]
        public IMess MethodInto3()
        {
            return _mess3;
        }

        #endregion

        public void EatFood()
        {
            Console.WriteLine("浙江人吃" + _mess2.GetFood());
        }
    }
}

输出:

5、ContainerControlledLifetimeManager单例

如果不清楚单例模式,请参考Sington(单例模式),Unity提供了单例模式,并将单例实例的生命周期叫给了对应的容器管理,代码如下:

 UnityContainer container = new UnityContainer();
 container.RegisterType<IMess, ZheJiangFood>("aa");
 IMess ee = container.Resolve<ZheJiangFood>("aa");
 IMess ee1 = container.Resolve<ZheJiangFood>("aa");
 Console.WriteLine("same instance?ansmer is {0}", object.ReferenceEquals(ee, ee1));
 Console.ReadLine();

修改第二行代码如下:

container.RegisterType<IMess, ZheJiangFood>("aa",new ContainerControlledLifetimeManager());

上面演示了将IMess注册为ZheJiangFood,并声明为单例,ContainerControlledLifetimeManager字面意思上就是Ioc容器管理声明周期,我们也可以不使用类型映射,将某个类注册为单例:

UnityContainer container = new UnityContainer();
ZheJiangFood food = new ZheJiangFood();
container.RegisterInstance<IMess>("aa",food,new ContainerControlledLifetimeManager());
IMess ee = container.Resolve<IMess>("aa");
IMess ee1 = container.Resolve<IMess>("aa");
Console.WriteLine("same instance?ansmer is {0}", object.ReferenceEquals(ee, ee1));
Console.ReadLine();

当我们声明一个类型为ContainerControlledLifetimeManager,说明该类型就是单例,所以当我们在程序中中获取该类型的实例时,IOC容器会返回上次创建的实例,而不会重新创建一个实例,这也是单例的精髓之处,但是具体的实例销毁时间,可能是容器销毁的时候,也可能是应用程序销毁的时候,具体我也不是很清楚.