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

标签 c# design-patterns dependency-injection inversion-of-control ioc-container

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

我的意思是,除了每次需要创建依赖对象并为其提供所有依赖项时都必须多敲几次键之外。

事实上,拥有像 DI 容器提供的中央注册表可能会更加困惑,并且会剥夺您的控制权。

这个问题和另一个问题(下面)我已经有一段时间了,但我想 Mark Seemann 的文章标题为 Service Locator is an anti-pattern让我终于在这里输入这个问题。

那么,我的另一个问题是:

如果有一个依赖项本身需要一些动态(比如用户提供的)输入来构建它怎么办?说,比如:

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);
    }
}

总的来说,您是否觉得 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();
  }
}

我不知道那个更新对之前的讨论有何影响。看起来就像是一些白痴(我)在提问之前没有在脑海中想出这个问题。提出问题本身会强制给出正确答案,@Maarten 证实了这一点并试图帮助我。

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

最佳答案

您不应该尝试通过调用容器来解析实例。您应该使用构造函数注入(inject)来注入(inject)您的依赖项。 Mark Seeman 将其称为 Don't call the container, let it call you .

要解决创建具有运行时值的注入(inject)类型实例的问题,请使用 abstract factories .

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

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!
    }
}

关于c# - 使用 new 运算符创建对象/依赖项与使用 DI 容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23652648/

相关文章:

spring - 什么时候我会使用@Bean而不是@Component?

java - 如何通过classes.methods调用来模仿@Inject?

c# - 使用 AsParallel

c# - 将自定义上下文菜单项添加到 Windows 窗体标题栏

c# - WebBrowser 无法到达 DocumentCompleted 事件

c++ - 这应该在模型中还是在 View 中?

c# - 如何将用户控件属性绑定(bind)到其他属性?

Python 将 Ajax 请求与普通页面 View 分开

oop - 继承和组合有什么区别?

c# - 多次创建 ASP.NET Core 单例服务