处置构造函数注入对象

时间:2010-12-29 21:07:21

标签: c# winforms dependency-injection idisposable

假设我有一个与另一个班级相关联的班级。它看起来像下面这样:

public class DisposableClassOne : IDisposable
{
   private class mDisposableClassTwo;

   public DisplosableClassOne(DisposableClassTwo dcTwoInjected)
   {
      mDisposableClassTwo = dcTwoInjected;
   }

   public void Dispose()
   {
      // Should I dispose here? or make caller dispose of dcTwoInjected
      //mDisposableClassTwo.Dispose();
   }
}

我应该调用Dispose的{​​{1}}方法,还是应该让调用者像这样处理它?<​​/ p>

mDisposableClassTwo

我正在考虑让调用者处理它是最好的方法,但我认为通过将调用置于Dispose方法中它可以保证它将被调用。有没有更好的方法来解决这个问题?

6 个答案:

答案 0 :(得分:5)

如果你创建的类<​​em>逻辑上拥有 (1)构造函数注入资源,那么它应该在内部处理它。如果它不拥有资源,那么它应该什么都不做,并依靠消费者决定何时应该处置它。

如果您的类拥有对非托管资源的引用,您可能还需要实现终结器(析构函数),因为无法保证任何人都可以调用您的Dispose方法。

通常,您希望避免调用者必须决定何时应该处理传递给类的构造函数的对象的情况。调用者可能过早地处理资源(或者保持不必要的时间)。在某些设计中实现这并不总是一件容易的事情......遗憾的是,一次性使用的对象模式并不总是与自身完美结合。


(1)通过所有权我的意思是你的类控制传递它的资源的生命周期,没有其他代码保存对它的引用。如果任何其他代码(其生命周期与您的类的生命周期无关)拥有对资源的引用并将独立使用它,那么您不是该资源的所有者

答案 1 :(得分:2)

我认为这是你必须在设计中做出的终生决定。

DisposableClassOne是否为Disposable,因为它引用了DisposableClassTwo?

DisposableClassTwo的生命周期是否与DisposableClassOne无关?

对我来说,这两个问题的答案在每个班级设计中都有所不同。 StreamReader / Writer是第一个问题是'是'和第二个'不'的一个很好的例子 - 读者完成后,没有人会期望在StreamReader中使用Stream,所以Reader处理它。

但是,如果DisposableClassTwo是其他一些资源 - 也许是一个文件引用,你依次传递给几个类'做某事'。在这种情况下,你不希望它在你准备好之前就已经处理掉了,DisposableClassOne可能早就过去了。

答案 2 :(得分:1)

我熟悉的标准是使用Disposable 模式。拥有虚拟Dispose(bool disposing)方法。 Dispose()方法调用Dispose(true),终结器调用Dispose(false)

然后,在主要处理方法中:

if (disposing)
{
    // Dispose of the referenced object, as well.
}

StreamWriter&amp; StreamReader遵循这种模式。如果您明确地致电Dispose(),他们也会处置基础Stream。如果让终结者进行清理,他们就不管它了。

或者,你可以添加一个属性:DisposeChild,如果为true,则处理子对象,如果没有,则不管它。

我也同意rcravens。在几乎所有情况下,对象都应该在实例化的同一范围内处理。

答案 3 :(得分:1)

要正确使用IDisposable对象,必须遵循“最后一个请关掉灯”的原则。有一些不错的场景,还有一个很好的场景:

  1. 对象的创建者将其提供给另一个对象(“收件人”),但第一个对象的创建者控制收件人的生命周期,并且可以知道收件人何时不再需要传递的对象。在这种情况下,原始对象的创建者负责Dispose。
  2. 对象的创建者将其提供给收件人,并且收件人知道在移交对象后没有其他人会使用该对象。在这种情况下,收件人将在完成对象时调用Dispose。
  3. 对象收件人有时可以以匹配#1的方式使用,有时以匹配#2的方式使用,但对象的供应商将知道何时应用哪种情况。可以通过告知收件人收件人是否应该处理它所给出的对象来处理这种情况。
  4. 供应商和收件人都希望使用该对象,但可能不知道哪个会比另一个更活跃。这是最艰难的情况。

我倾向于喜欢#3中提出的方法。但有时情况#4适用。处理的一种方法是对象的接收者在完成对象时触发事件。该事件可以Interlocked.Decrement一个计数器,指示仍有多少对象仍在使用传递的对象。一旦该计数器达到零,就可以删除该对象。

答案 4 :(得分:0)

最好不要配置任何外部引用,这些引用将由调用者自动处理。

请参阅此主题,其中建议仅需要处理成员变量:

Finalize/Dispose pattern in C#

答案 5 :(得分:0)

“所有者清理”的概念太常见而不容忽视。所有权必须不仅仅是巧合。

如果您想要更易于维护的软件,请避免可能被错误解释的副作用。 Stream vs StreamReader示例非常棒......两者都是IDisposable,而作为调用者,我正在制作两者,所以我绝不应该给自己一个处理它们中的任何一个的通行证。这样做会违反抽象,将我的调用代码的知识与流类的实现细节联系起来(这可能会随着时间的推移而在学术上发生变化)。

最简单的答案是使用两个usings(),即使你然后去制作(和文档!)第二个IDisposable将处理第一个。