代码改变世界

IOC注入框架——Unit简单依赖注入

2012-10-27 21:44  C#与.NET探索者  阅读(753)  评论(0编辑  收藏  举报

Unit简单依赖注入

准备工作:还是上节课的几个类:
//抽象类,播放器
public abstract class Player
{
    public abstract string Play(); //播放功能
}

//MP3播放器
public class MP3Player : Player
{
    public override string Play()
    {
        return "this is MP3Player";
    }
}

//CD播放器
public class CDPlayer : Player
{
    public override string Play()
    {
        return "this is CDPlayer";
    }
}

//DVD播放器
public class DVDPlayer : Player
{
    public override string Play()
    {
        return "this is DVDPlayer";
    }
}
一、构造子注入(Constructor Injection)
当我们使用Unity的Resove方法动态构造一个对象A的时候,有可能会出现这样的问题:该类的构造函数的形参依赖于另外一个类的对象B(即,形参列表中要接收另外一个对象的实例 A->B)。
这样在动态生成对象A之前必须要先动态生成对象B,然后再把对象B注入到A的构造函数中,生成A对象。这就是我们所说的构造子注入。
如:再加入下面一个类
public class ClassRoom
{
    private Player player; //播放器的抽象
    public ClassRoom(Player player) //构造函数,接收一个Player类型的对象
    {
        this.player = player;
    }
    public string Show()    //返回播放器的play()结果
    {
        if (player != null)
            return this.player.Play();
        else
            return "No Player";
    }
}
这个类的构造函数中依赖于Play对象。意味着我们在动态生成ClassRoom对象之前要动态生成Player对象。
客户类:
public partial class TempDefault : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        IUnityContainer container = new UnityContainer();
        container.RegisterType<Player, MP3Player>();
        ClassRoom cr = container.Resolve<ClassRoom>();
        Response.Write(cr.Show());
    }
}
运行结果:


分析:在客户类中ClassRoom cr = container.Resolve<ClassRoom>();动态生成ClassRoom对象,在执行ClassRoom构造函数的时发现ClassRoom对象依赖于Player对象。因此它会在container容器中去检索Player的映射,由于我们在此之前加使用container.RegisterType<Player, MP3Player>();把Player映射到MP3Player类上,所以它会先动态生成MP3Player对象,然后注入到ClassRoom的构造函数中生成ClassRoom对象,所以在这里player.Show()方法中调用的应当是MP3Player对象的Show方法。
如果把container.RegisterType<Player, MP3Player>();注释掉,则依赖注入会失败,产生“未将对象引用设置到对象实例”的异常。
说明:
    Unity对于单个构造器的情况,将做自动的依赖注入。
    多个构造器的情况下,加有[InjectionConstructor]标签的构造器为依赖注入的构造器,如果没有任何构造器有贴上[InjectionConstructor],则使用参数最多的构造器作为依赖注入的构造器
    public class ClassRoom
    {
        private Player player;
        public ClassRoom(Player player)
        {
            this.player = player;
        }
        [InjectionConstructor]
        public ClassRoom(CDPlayer player)
        {
            this.player = player;
        }
        public string Show()
        {
            if (player != null)
                return this.player.Play();
            else
                return "No Player";
        }
    }
    运行结果:

    如果在IUnityContainer容器中注册的是一个对象实例,那在在生成ClassRoom对象的时候就不会再次动态构造形参对象,而是直接用已注册的对象实例来实例化ClassRoom对象。
    客户类:
    public partial class TempDefault : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            IUnityContainer container = new UnityContainer();
           DVDPlayer player = new DVDPlayer();
            container.RegisterInstance<Player>(player);
            ClassRoom cr = container.Resolve<ClassRoom>();
            Response.Write(cr.Show());
        }
    }
    运行结果:


   
二、属性设置注入(Property Setter Injection)
    如果我们动态生成某个对象A的时候,想同时为A的某个属性赋值,而该属性又依赖于另一个对象B(即,该属性的类型是类型B),这时就应当使用我们的属性注入。
    如果存在属性注入,那它会先动态生成依赖的对象B,然后再把B依赖注入到对象A的属性中去,返回对象A。
    属性注入的注法是在属性的上面加上[Dependency]标签。
    如果ClassRoom类变为:
    public class ClassRoom
    {
        private Player player;
        public Player Player
        {
            get
            {
                return player;
            }
            set
            {
                player = value;
            }
        }
        public string Show()
        {
            if (player != null)
                return this.player.Play();
            else
                return "No Player";
        }
    }
    运行结果:
   


    我们发现并没有使用public Player Player{get;set;}属性初始化成员变量。
    如果ClassRoom类变为:
    public class ClassRoom
    {
        private Player player;
        [Dependency]
        public Player Player
        {
            get
            {
                return player;
            }
            set
            {
                player = value;
            }
        }
        public string Show()
        {
            if (player != null)
                return this.player.Play();
            else
                return "No Player";
        }
    }
    运行结果:
    

    
    如果在IUnityContainer容器中注册的是一个对象实例,那在在生成ClassRoom对象的时候就不会再次动态构造形参对象,而是直接用已注册的对象实例来实例化ClassRoom对象。
   
三、方法调用注入(Method Call Injection)
    如果我们动态生成某个对象A的时候,想同时调用A的某个方法,而该方法的形参又依赖于另一个对象B(即,该方法形参的类型是类型B),这时就应当使用我们的方法调用注入。
    如果存在方法调用注入,那它会先动态生成依赖的对象B,然后再把B依赖注入到对象A的方法形参中去,返回对象A。
    方法调用注入的注法是在方法的上面加上[InjectionMethod]标签。
    如果ClassRoom类变为:
    public class ClassRoom
    {
        private Player player;
        [InjectionMethod]
        public void Init(Player player)
        {
            this.player = player;
        }
        public string Show()
        {
            if (player != null)
                return this.player.Play();
            else
                return "No Player";
        }
    }(车延禄)
    运行结果:


    如果在IUnityContainer容器中注册的是一个对象实例,那在在生成ClassRoom对象的时候就不会再次动态构造形参对象,而是直接用已注册的对象实例来实例化ClassRoom对象。
   
四、防止循环依赖
在介绍 Constructor Injection、Property Injection 和 Method Call Injection 时,都有特别提到不要出现循环引用(Circular References),因为出现这种问题后很难去检测到。最好的解决方法是写代码时候尽量避免出现这种情况。
    1.避免通过Constructor Injection生成的对象在构造器的参数中互相引用,以下是错误用法:
    public class Class1
    {
        public Class1(Class2 test2)
        {
            ..
        }
    }
    public class Class2
    {
        public Class2(Class1 test1)
        {
           
        }
    }
    2. 避免通过Constructor Injection生成的对象作为自身构造器的参数,以下是错误用法:
    public class Class1
    {
        public Class1(Class1 test1)
      { }
    }
    3. 避免通过method call injection生成的对象互相引用,以下是错误用法:
    public class Class1
    {
        [InjectionMethod]
        public void Method1()
        {
            Method2();
        }

        [InjectionMethod]
        public void Method2()
        {
            Method1();
        }
    }
    4.避免通过property(setter) injection生成的对象互相引用,以下是错误用法:
    public class Class1
    {
        [Dependency]
        public string Propert1
        {
            get
            {
                return Propert2;
            }
        }

        [Dependency]
        public string Propert2
        {
            get
            {
                return Propert1;
            }
        }
    }
(车延禄)