【问题标题】:Is property injection considered to be bad?财产注入被认为是坏事吗?
【发布时间】:2017-01-27 14:27:38
【问题描述】:

演示问题的示例解决方案:

class World
{
    public override string ToString()
    {
        return "Hello World";
    }
}
class Hello
{
    [Inject]
    public World theWorld {  get;  set; }
    public Hello(IKernel kernel)
    {
        kernel.Inject(this);
    }
    public override string ToString()
    {
       return theWorld.ToString();
    }
}
class Program
{
    static IKernel kernel = new StandardKernel();
    static void RegisterServices()
    {
        kernel.Bind<World>().ToSelf();
    }
    static void Main(string[] args)
    {
        RegisterServices();
        Hello hello = new Hello(kernel);
        Console.WriteLine(hello.ToString());
        Console.ReadLine();
    }
}

这就是我让属性注入真正起作用的方式。

如果出现以下情况,它将不起作用:

  1. 属性不是公开的(或其设置者)。
  2. 请求注入的类没有得到IKernel实例,调用kernel.Inject(this);

对我来说,仅仅为了获取一个属性的实例而这样做似乎是非常过分和错误的。有没有更简单的方法或者我没有考虑过什么?

【问题讨论】:

    标签: c# .net ninject


    【解决方案1】:

    Constructor Injection 通常是比Property injection 更有利的技术,因为属性注入会导致Temporal Coupling code smell。因此,属性注入应该仅用于真正可选的依赖项(在您的情况下并非如此)。然而,依赖关系几乎不应该是可选的。即使在没有实现依赖的情况下,与注入 null 引用相比,创建和注入 Null Object 实现通常更好。与其使用属性注入,不如通过构造函数注入所有必需的依赖项。

    另一种导致相当大缺点的做法是,当您让应用程序代码依赖于 DI 容器本身(或表示容器的解析 API 的抽象)时。这是一个名为Service Locator 的反模式。您应该引用容器的唯一位置是在您的Composition Root 内。您示例中的 Program 类表示合成根。

    您的 Hello 类应该简单地接受 World 作为必需的构造函数参数:

    class Hello
    {
        private readonly World theWorld;
    
        public Hello(World world)
        {
            this.theWorld = world ?? throw new ArgumentNullException("world");
        }
    
        public override string ToString()
        {
            return this.theWorld.ToString();
        }
    }
    

    请注意对容器的任何引用是如何从此类中完全删除的。这使得类更简单、更可维护、更可测试,甚至可以在不使用 DI Container 的情况下组合此类;一种通常称为Pure DI 的做法。当您的应用程序较小时,Pure DI 甚至是比使用容器更好的选择。

    这是您的 Program 类在使用 Ninject 时的样子:

    class Program
    {
        static void Main(string[] args)
        {
            // Configure
            var kernel = new StandardKernel();
            kernel.Bind<Hello>().ToSelf();
            kernel.Bind<World>().ToSelf();
    
            // Resolve
            var hello = kernel.Get<Hello>();
    
            // Use
            Console.WriteLine(hello.ToString());
            Console.ReadLine();
        }
    }
    

    没有容器的情况如下:

    class Program
    {
        static void Main(string[] args)
        {
            // Resolve
            var hello = new Hello(new World());
    
            // Use
            Console.WriteLine(hello.ToString());
            Console.ReadLine();
        }
    }
    

    【讨论】:

    • 我将此作为我的#wheresWaldo DI 问题/问题的一部分。构造注入很容易确保它已连接。我无法计算因#whereWaldo 问题而损失的小时数。很好的答案在这里。 +1 组合根。 +1 链接到时间问题。
    • @alex。这是注入的 NULL 混凝土的具体示例。 (正如史蒂文在这里讨论的那样)docs.microsoft.com/en-us/dotnet/api/…
    猜你喜欢
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-07
    • 1970-01-01
    • 2020-11-15
    相关资源
    最近更新 更多