【问题标题】:Is IoC typically implemented as a singleton? How is IoC implemented?IoC 通常是作为单例实现的吗? IoC是如何实现的?
【发布时间】:2012-10-25 11:14:53
【问题描述】:

我计划深入研究其中一个开源 IoC 容器以真正解决 100% 的问题,但我想我也会询问一般社区(在无法在任何密切相关的问题中找到直接答案之后) )。

就我理解的典型 IoC 实现而言,它似乎是一个全局类,充当具有所有依赖项知识的单例。然后它使用这些知识来提供构造函数或属性参数,它知道如何填充它们?也许我错过了一些东西,因此是这个问题。

谁能明确地告诉我 IoC 是如何工作的和/或它是否是一个单例?

更新

我想我的问题是“神奇”的 IoC 是如何像 Ninject.MVC 一样工作的?注射在哪里“有效”?

【问题讨论】:

  • Ninject.MVC 只能工作,因为 MVC 框架提供了 DependencyResolver.SetResolver() 扩展点。如果这不存在,那么任何 IoC 容器都不能与 MVC 一起“正常工作”。

标签: .net design-patterns dependency-injection inversion-of-control ioc-container


【解决方案1】:

所有主要的 DI 框架(或至少在 Java 和 .NET 中)通常建议在应用程序的生命周期内拥有单个容器实例。一些容器确实支持“子容器”的概念,但这些子容器是从单个容器创建的,实际上只是同一个实例的一部分。

绝对可以有多个容器,例如每层或每个会话一个容器,但是当您根据依赖注入原则设计应用程序时,通常每个应用程序使用一个容器可以获得最佳结果(在.NET 的上下文,这将是每个应用程序域,或者通常是在同一内存空间中运行的所有代码)。在处理由(桌面)客户端和 Web 服务组成的应用程序时,两者都有自己的容器(因为它们实际上是不同的程序,彼此不知道)。

虽然可以为每个(Web 用户)会话、请求或类似的东西定义一个容器实例,但这往往会使事情复杂化,因为很难注册比会话更长的生命周期的依赖项,并且在创建容器时会产生很多性能开销。

【讨论】:

    【解决方案2】:

    IoC 容器往往不是单例的,因为您可能希望同时运行多个容器(例如,系统的每一层一个),尽管这不是最常见的做法.

    为了访问您的容器,您需要对实际容器本身的引用。

    即使您的容器不是真正的单例,您也可以使用服务定位器访问它(另请参阅http://msdn.microsoft.com/en-us/library/ff921142(v=pandp.20).aspx)。

    【讨论】:

    • 那么,您将容器本身传递给控件吗?深入研究时,这似乎也不是很好。在我看来,这几乎是 ServiceLocator 模式?
    • 您可以将 ServiceLocator 与 IoC 容器结合使用,例如此处的示例:blogs.msdn.com/b/miah/archive/2009/05/12/…
    • 而且,不,您不会传递容器。您配置容器来管理类的实例化,然后使用属性注入或构造函数注入。您只能在特定位置调用容器。任何好的 IoC 教程都会教你如何正确使用它。
    • 你能提供一个好的IoC教程吗?我使用了 Ninject.MVC,它“可以正常工作”,并且在不知道底层实现的情况下使用它(我现在将这样做)。现在,我正在尝试将 IoC (Ninject) 与 WPF 和 WinForms 一起使用。所以,我想弄清楚是否有任何方法可以避免更新子窗口或使用 kernel.Get 之类的东西,因为它似乎是定位器模式?我将继续寻找这些示例,但似乎除非您开始传递您的定位器(内核),否则事情会超出第一级?
    • -1 大多数应用程序确实只有一个容器,但没有单例(每个模式定义)。每次创建一个新容器都需要每次都分析和生成所有映射,并且恕我直言,使用容器的方法不是很有效。大多数容器确实有可能创建子容器,因此能够提供自定义生命周期。
    【解决方案3】:

    没有理由让它成为单例。仅仅因为您将实体通过上下文连接在一起,您仍然可以运行多个上下文(使用 Spring 白话,但不将参数限制为 Spring)

    请注意,像 Java 这样的平台甚至不能强制执行单例(由于它们的多个类加载器架构)。

    【讨论】:

      【解决方案4】:

      好的,在进一步浏览网上并与同事交谈之后,我相信如果没有静态单例,就无法实现 IoC(至少在有状态程序..winforms/wpf 中)。因此,我计划使用组合根模式,并以服务定位器的方式将 ninject 内核用作静态单例。我仍然会调用 kernel.Get,但我想我至少不再关心该项目中的依赖项。我只是在兜圈子:)。

      类似这样的:

      Main
      {
        setupkernel();
        Application.Run(kernel.Get<Main>);
      }
      
      btn_GetChildForm()
      {
          kernel.Get<ChildForm>().Show();
      }
      

      如果有人知道更好的方法,请告诉我。否则,这是我拼凑起来的最佳方法。

      【讨论】:

      • DI 纯粹主义者建议避免使用 Service Locator(我也是如此),但你永远不应该仅仅因为有人推荐它而做任何事情 :) 如果你想避免使用 Service Locator,你可以将你的容器隐藏在适当的抽象后面你创造的。有关示例,请参阅this answer,但它可能比您需要的更复杂,因为它涉及范围界定问题。 (适用于 Autofac,但 Ninject 类似。)
      【解决方案5】:

      Service Locator is an Anti-Pattern

      尝试了解“组合根”的性质。

      【讨论】:

      • 是的,其他答案已经涵盖了这一点。您的回答对进一步的对话和/或帮助没有任何帮助,因为即使是组合根也已经讨论过......请在尝试没有更多细节的笼统陈述之前阅读其他答案
      • 你确定吗?我所看到的只是对服务位置的倡导和对 IoC 原则的误解。
      • 是的,在一定程度上倡导 SL(请记住,没有什么是非黑即白的,反模式通常是不好的,但并非总是如此)如果你能告诉我如何完成 Composition Root在不是 SL 的客户端应用程序(Winform/WPF)中,请更新您的答案,否则,请查看我的答案以了解在这种情况下的妥协。
      • Mark Seeman 正是您正在寻找的示例,它正确实现了组合根的概念,然后使用抽象工厂解决了依赖关系。我认为您可以在他的书随附的网站上获得 wpf 示例的来源。您的示例并不是真正的组合根,因为您随后使用 sl 来解析。所有解析都应该在组合根中完成,或者推迟到在组合根中解析的抽象工厂。
      猜你喜欢
      • 2012-10-07
      • 1970-01-01
      • 1970-01-01
      • 2013-07-16
      • 2015-10-09
      • 2016-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多