属性 注入被认为是不好的?

Is property injection considered to be bad?

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

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. 属性 不是 public(或其 setter)。
  2. 请求注入的class没有得到IKernel实例,调用kernel.Inject(this);.

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

Constructor Injection is typically a more favorable technique than Property injection, because Property Injection causes the Temporal Coupling code smell. Property Injection should, therefore, only be used for dependencies that are truly optional (which it isn't in your case). Dependencies, however, should hardly ever be optional. Even in case there is no implementation for a dependency, it's generally better to create and inject a Null Object 实现与注入 null 引用相比。与其使用 属性 注入,不如通过构造函数注入所有必需的依赖项。

导致相当大缺点的另一种做法是让您的应用程序代码依赖于 DI 容器本身(或表示容器的 resolve-API 的抽象)。这是一种称为 Service Locator. The only place you should reference the container is inside your Composition Root 的反模式。您示例中的 Program class 表示合成根。

相反,您的 Hello class 应该简单地接受 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();
    }
}

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

这是您的 Program class 使用 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();
    }
}