没有托管堆可以实现DI吗?

时间:2017-02-04 22:37:19

标签: c# c++ memory-management com inversion-of-control

通过依赖注入,一个班级'依赖项由调用者实例化并传入,通常作为构造函数参数。这适用于具有托管堆的语言,因为无需担心依赖关系的生命周期结束。但是其他类型的语言呢?

例如,在传统的mallocfree环境中,分配内存的方法通常也应该释放它。我不确定如何通过DI实现这一目标。

或者使用需要引用计数的存储器方案,例如COM,我不确定调用者何时会在依赖项上调用Release,或者接收注入的对象应该调用Release两次。

是否可以在没有托管堆的情况下使用DI?如果是这样,哪些代码模式可以很好地确保资源正确释放?

2 个答案:

答案 0 :(得分:1)

对我来说DI和垃圾收集是无关的。您只需要组织内存管理。从尝试到处使用静态对象开始。如果您的DI图是静态的(即在应用程序工作期间它没有改变),这将起作用。

如果您的DI图表正在发展或在应用程序启动期间确定其结构,则需要有关如何执行此操作的更多信息。我不确定适合每种情况的一般解决方案是否可行。修改DI图的实体应负责清理。完全与在其他情况下在C ++中完成的方式相同。例如,您可以将DI代理对象放入一个静态容器中,该容器将在应用程序工作结束时被销毁(如果所有内容都已正确组织其内容)。

在C / C ++中,需要以这种或那种方式管理内存管理容器,连接和其他许多东西。我不明白为什么DI很特别。垃圾收集解决了它所用的所有内容的内存管理(仍有其自身的缺点)。

答案 1 :(得分:1)

  

但是其他类型的语言呢?是否可以在没有托管堆的情况下使用DI?

拥有托管堆不是DI的先决条件。例如,C ++不是托管语言,但是它有DI框架,可与用于托管语言(如Java)的DI框架的功能相媲美C#。

Daniele Pallastrelli的精彩演讲Going native with less coupling - Dependency Injection in C++详细解释了DI对其他两种解耦技术(工厂和服务定位器)的好处。它还提供了一个名为Wallaroo的C ++ DI框架,并解释了它的内部结构。

另一个C ++ DI框架,基于不同的方法,然后Wallaroo是[Boost].DI。我强烈建议您阅读“简介”一章。它为“我是否已经使用依赖注入?”,“我是否需要依赖注入?”等问题提供了简短但很好的答案。

我想提到的第三个C ++ DI框架是Infector++

这些只是众多C ++ DI框架中的三个。您可以在this page上找到大量列出的内容。

我的观点是,如果有很多用于C ++的DI框架,无论它们是否被广泛接受,肯定有可能没有托管堆的DI: - )

  

如果是这样,哪些代码模式可以很好地确保资源正确释放?

上面的链接提供了关于如何在C ++中完成DI框架的额外输入,包括依赖项解析,不同的创建策略和对象范围,最后是您的问题,即对象生命周期管理。

在这里,我将简要概述生命周期管理如何一致且确定性地完成。所有提到的框架都大量使用智能指针(std::unique_ptrstd::shared_ptr,如果它们提供Boost支持,也使用boost::shared_ptr并将创建策略语义附加到它们。请注意,您不需要完整的DI框架来使用此模式。基本思路很简单。

假设我声明了一个类似下面的类:

class i_depend_on_others {
    i_depend_on_others(std::unique_ptr<other>,
                       std::shared_ptr<another_other>,
                       boost::shared_ptr<yet_another_other>)
    { }
};

这是一个明确的构造函数注入,但附加了关于“其他”的预期生命周期的语义。第一个other将由i_depend_on_others实例拥有,由于我们拥有std::unique_ptr,因此只要删除i_depend_on_others实例,它就会被删除。 another_otheryet_another_other的生命周期应与i_depend_on_others实例无关。这种模式清楚地定义了何时i_depend_on_others实例负责清理资源以及何时调用代码应该这样做。 (在DI框架的情况下,框架负责共享实例。)

问题是在这种情况下该怎么做:

class i_depend_on_others_as_well {
    i_depend_on_others_as_well(other*) { }
};

(我将在此处讨论在现代C ++开发中应避免使用原始指针。假设我们被迫使用它们。) 同样,该模式定义了明确的语义。 原始指针意味着所有权转移。 i_depend_on_others_as_well的实例负责删除other

对于像[Boost].DI这样的DI框架,指针的类型将决定注入对象的默认生命周期。对于共享指针,它们将是singeltons,创建一次并由[Boost] .DI维护,对于原始指针和唯一指针,每次都会创建一个新实例。

可以在[Boost] .DI文档的"Decide the life times"章节中找到有关此模式的更详细说明。