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


    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;
                }
            }
        }
    (车延禄)

  • 相关阅读:
    像画笔一样慢慢画出Path的三种方法(补充第四种)
    占位符行为 PlaceHolderBehavior 的实现以及使用
    WPF实现物理效果 拉一个小球
    WPF实现Twitter按钮效果
    WPF自适应可关闭的TabControl 类似浏览器的标签页
    WPF仿百度Echarts人口迁移图
    WPF绘制简单常用的Path
    51Nod 1534 棋子游戏
    数论基础
    Buy a Ticket
  • 原文地址:https://www.cnblogs.com/zpc870921/p/2742936.html
Copyright © 2020-2023  润新知