5

感谢您的关注。我对 Ninject 有点陌生,到目前为止还很喜欢。我得到了在调试模式下绑定一个东西并在发布模式下绑定另一个东西的部分。这些是全局绑定,您必须使用 Ninjects 示例代码声明每个武士都将拥有一把剑或一把匕首。这不是非此即彼的,它是一个或另一个。

我该怎么做才能让一个武士带剑,另一个带匕首,如果他们愿意,他们甚至可以切换武器。除了创建一堆具有不同绑定模块的内核之外,还有其他方法吗?

这是来自 Ninject 的示例代码。如果你将它放在控制台应用程序中,它应该运行:

using System;
using Ninject;

namespace NinjectConsole
{
    class Program
    {

        //here is where we have to choose which weapon ever samurai must use...
        public class BindModule : Ninject.Modules.NinjectModule
        {
            public override void Load()
            {
                //Bind<IWeapon>().To<Sword>();
                Bind<IWeapon>().To<Shuriken>();
            }
        }

        class Shuriken : IWeapon
        {
            public void Hit(string target)
            {
                Console.WriteLine("Pierced {0}'s armor", target);
            }
        }

        class Sword : IWeapon
        {
            public void Hit(string target)
            {
                Console.WriteLine("Chopped {0} clean in half", target);
            }
        }

        interface IWeapon
        {
            void Hit(string target);
        }

        class Samurai
        {
            readonly IWeapon weapon;

            [Inject]
            public Samurai(IWeapon weapon)
            {
                if (weapon == null)
                    throw new ArgumentNullException("weapon");

                this.weapon = weapon;
            }

            public void Attack(string target)
            {
                this.weapon.Hit(target);
            }
        }

        static void Main(string[] args)
        {

            //here is where we bind...
            Ninject.IKernel kernel = new StandardKernel(new BindModule());

            var samurai = kernel.Get<Samurai>();
            samurai.Attack("your enemy");

            //here is I would like to do, but with DI and no local newing up...
            var warrior1 = new Samurai(new Shuriken());
            var warrior2 = new Samurai(new Sword());
            warrior1.Attack("the evildoers");
            warrior2.Attack("the evildoers");
            Console.ReadKey();
        }
    }
}

编辑

感谢重播和建议。

我想出了如何得到我想要的东西。好的,这就是我所做的:

  1. 将默认/初始绑定设置为最弱的武器。有点像 new-b 会做的。
  2. 添加了另一种武器(匕首)
  3. 扩展了 IWeapon 以包括 WeaponHitPoints 值来评估武器值。
  4. 扩展了 Samurai 以包括添加和丢弃武器的方法,以便 Samurai 可以获得或丢失武器。
  5. 修改了攻击方法以使用最好的武器。
  6. 修改了程序以利用添加的功能。
  7. TODO:添加 try/catch 和 null 检查...

创建一个名为 NinjectConsole 的控制台项目,安装 Ninject,您应该能够将其放入并运行它。

这是新代码:

using System;
using System.Collections.Generic;
using System.Linq;
using Ninject;

namespace NinjectConsole
{
    class Program
    {
        public class BindModule : Ninject.Modules.NinjectModule
        {
            // default bind to weakest weapon 
            public override void Load()
            {
                Bind<IWeapon>().To<Dagger>();
            }
        }

        class Dagger : IWeapon
        {
            public int WeaponHitPoints { get { return 5; } }
            public string Hit(string target)
            {
                return String.Format("Stab {0} to death", target);
            }
        }

        class Shuriken : IWeapon
        {
            public int WeaponHitPoints { get { return 9; } }

            public string Hit(string target)
            {
                return String.Format("Pierced {0}'s armor", target);
            }
        }

        class Sword : IWeapon
        {
            public int WeaponHitPoints { get { return 11; } }

            public string Hit(string target)
            {
                return string.Format("Chopped {0} clean in half", target);
            }
        }

        interface IWeapon
        {
            int WeaponHitPoints { get; }
            string Hit(string target);
        }

        private class Samurai
        {
            private IEnumerable<IWeapon> _allWeapons;

            public Samurai(IWeapon[] allWeapons)
            {
                if (!allWeapons.Any())
                    throw new ArgumentException("Samurai");

                _allWeapons = allWeapons;
            }

            public void AddWeapon(IWeapon weapon)
            {  //TODO: check for nulls...
                _allWeapons = _allWeapons.Concat(new[] { weapon });
            }

            public void DropWeapon(IWeapon weapon)
            {  //TODO: check for nulls...
                Console.WriteLine("A Samurai got rid of a " + weapon.WeaponName);

                _allWeapons = _allWeapons.Where(x => x.WeaponName != weapon.WeaponName);
            }

            public void Attack(string target)
            {
                int points = 0;

                try
                {
                    points = _allWeapons.Max(x => x.WeaponHitPoints);
                }
                catch ()
                {
                    Console.WriteLine("You just punched " + target + " on the nose!");
                }

                var attackWeapon = _allWeapons.FirstOrDefault(i => i.WeaponHitPoints == points);

                //TODO: check for nulls... 
                Console.WriteLine(attackWeapon.Hit(target));
            }
        }

        static void Main(string[] args)
        {
            Ninject.IKernel kernel = new StandardKernel(new BindModule());

            var samurai1 = kernel.Get<Samurai>();
            var samurai2 = kernel.Get<Samurai>();

            Console.WriteLine("Samurai #1");
            samurai1.Attack("your enemy");

            samurai2.AddWeapon(new Shuriken());

            Console.WriteLine("\nSamurai #2 selects best weapon for attack");
            samurai2.Attack("your enemy");

            Console.WriteLine("\nSamurai #1 gets new weapon!");
            samurai1.AddWeapon(new Sword());

            Console.WriteLine("Samurai #1 selects best weapon for attack");
            samurai1.Attack("your enemy");

            Console.ReadKey();
        }
    }
}
4

2 回答 2

4

一般来说,除非您指定某些条件,否则您无法使用 IOC 容器来实现这一点,这些条件应该满足才能选择正确的实现(武器)。容器需要知道在当前情况下选择哪一种实现。

我建议,您正在寻找某种Contextual binding

Ninject 中有很多条件绑定方法(参见上面的链接)。我选择了命名绑定,因为它是一个非常简单的示例。

命名绑定

根据配置的名称解析依赖关系。

kernel.Bind<Samurai>().ToSelf().Named("SwordMaster");
kernel.Bind<Samurai>().ToSelf().Named("ShurikenMaster");

kernel.Bind<IWeapon>().To<Sword>().WhenParentNamed("SwordMaster");
kernel.Bind<IWeapon>().To<Shuriken>().WhenParentNamed("ShurikenMaster");

warrior1 = kernel.Get<Samurai>("SwordMaster");
warrior2 = kernel.Get<Samurai>("ShurikenMaster");

多次注射

如果您希望Samurai能够处理多种武器,您可以声明多个绑定,并且可以将它们作为集合IWeapon注入。Samurai

public Samurai(IEnumerable<IWeapon> weapons)
{
     this.AllMyWeapons = weapons;
}
于 2013-02-06T22:42:59.157 回答
1

不幸的是,尽管 Ninject 文档使理解容器的语法变得很容易,但它也可能给人以错误的印象,即何时应该使用 IoC 容器。

这个想法是您的服务是从 IoC 容器注册和解析的 - 但 aSamurai并不是真正的服务,它是一个域对象。这些应该由(例如)a SamuraiFactory(现在有一个可怕的想法......)

解析服务时,预计您只需在初始化期间注入一次组件。这是唯一一次使用 IoC 容器 - 理想情况下,您应该只调用 .Resolve() 一次,以启动您的依赖网络。当你有你的组合 root时,你就有了程序的入口点——从那时起,你不再引用 IoC 容器并且你的程序正常执行。

于 2013-02-07T18:01:25.820 回答