使用new运算符创建对象/依赖项与使用DI容器

Wat*_* v2 5 c# design-patterns dependency-injection ioc-container inversion-of-control

通过构造函数注入手动连接依赖项有什么不好,使用DI容器(container.Resolve<T>())的那个有什么好处呢?

我的意思是,除了每次需要创建依赖对象并为其提供所有依赖项时必须再键入一些键击的事实.

实际上,拥有一个像DI Container这样的中央注册表可能会让你感到更加困惑并从中获取控制权.

我已经有了这个问题和另一个(下面)有一段时间,但我猜Mark Seemann的文章名为Service Locator是一个反模式让我最终在这里输入这个问题.

所以,我的另一个问题是:

如果某个依赖项本身需要一些动态(比如用户提供的)输入,那该怎么办?说,像:

class Dependency : IDependency
{
  public Dependency(string userInput) { }
}

class Dependent
{
    IDependency _dependency;

    void DoSomething()
    {
      var container = new MyFavoriteContainerFromTheMarket();

      Console.Write("Hey, user. How are you feeling?");
      var userInput = Console.ReadLine();

      // Do DI containers have overloads for Resolve instead of for Register
      // that accept parameters to be passed to constructors?
      // In other words, what if I don't know the constructor parameter
      // value at the time of registration of the dependency but only
      // get to know it just before I have to resolve/instantiate the
      // dependency? Do popular DI containers of today have overloads
      // for that? I assume they must
      _dependency = container.Resolve<IDependency>(userInput);
    }
}
Run Code Online (Sandbox Code Playgroud)

总的来说,感觉DI容器不会夺走你的控制权吗?在某些情况下手动连接依赖项不是没有意义吗?

当然,我理解有时它很容易并且可以节省您的输入并创建一个更干净,更简洁的代码片段,但让它全部混淆 - 一些由容器管理的依赖项和一些您自己手动提供的依赖项 - 一切都让它如此混乱,迫使你记住哪一个是哪个,从而使维护变得更加困难,你不同意吗?

更新

等一下.我刚刚意识到我的整个例子令人费解.我原本打算从main()中的客户端代码调用Resolve.我最初应该用这个问题发布的代码应该是这样的:

class Dependency : IDependency
{
  public Dependency(string userInput) { }
}

class Dependent
{
    IDependency _dependency;

    public Dependent(IDependency dependency)
    {
      _dependency = dependency;
    }

    public void DoSomething()
    {
    }
}

class Program
{
  public static void Main(string[] args)
  {
      var container = new MyFavoriteContainerFromTheMarket();

      container.Register<IDependency>(new Dependency(/* I want this to come later */));

      container.Register<Dependent>(new Dependent(), 
       new ConstructorInjection<IDependency>());

      Console.Write("Hey, user. How are you feeling?");
      var userInput = Console.ReadLine();

      // It appears I can't do this as it makes perfect sense
      // to have the whole tree of dependencies known at the time
      // of registration.
      var dependent = container.Resolve<Dependent>(userInput);

      dependent.DoSomething();
  }
}
Run Code Online (Sandbox Code Playgroud)

我不知道那次更新对此前的讨论有什么影响.看起来好像有些白痴(我)在询问之前没有脑子里的问题.询问问题本身就迫使正确的答案,@ Maarten确认并试图帮助我.

对不起,@ Maarten.我浪费了你的时间.:-)

Maa*_*ten 4

您不应该尝试通过调用容器来解析实例。您应该使用构造函数注入来注入依赖项。Mark Seeman 将此称为“不要调用容器,让它调用您”

要解决创建具有运行时值的注入类型实例的问题,请使用抽象工厂

所以你的例子可以这样解决:

class Dependency : IDependency
{
  public Dependency(string userInput) { }
}

interface IDependencyFactory {
    IDependency Create(string userInput);
}

class DependencyFactory: IDependencyFactory {
    IDependency Create(string userInput) {
        return new Dependency(userInput);
    }
}

class Dependent {
    public Dependent(IDependencyFactory factory) {
        // guard clause omitted
        _factory = factory
    }

    private readonly IDependencyFactory _factory;

    void DoSomething() {
        // Do not call the container, let it call you.
        // So no container usage here.
        //var container = ...

        Console.Write("Hey, user. How are you feeling?");
        var userInput = Console.ReadLine();

        var dependency = _factory.Create(userInput);
        // There you go!
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这样一来,这个讨论就变得有点困难了。总结:我开始了解关于 DI 的 2 条规则: 1)不要调用容器,让容器注入您的可注入对象 2)如果您需要一个运行时值来创建可注入对象的实例,那么容器无法生成它,因此创建一个可以在需要时生产它的抽象工厂,并将该工厂注册到容器中。 (2认同)