假设我有一个与另一个班级相关联的班级。它看起来像下面这样:
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方法中它可以保证它将被调用。有没有更好的方法来解决这个问题?
答案 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对象,必须遵循“最后一个请关掉灯”的原则。有一些不错的场景,还有一个很好的场景:
我倾向于喜欢#3中提出的方法。但有时情况#4适用。处理的一种方法是对象的接收者在完成对象时触发事件。该事件可以Interlocked.Decrement一个计数器,指示仍有多少对象仍在使用传递的对象。一旦该计数器达到零,就可以删除该对象。
答案 4 :(得分:0)
答案 5 :(得分:0)
“所有者清理”的概念太常见而不容忽视。所有权必须不仅仅是巧合。
如果您想要更易于维护的软件,请避免可能被错误解释的副作用。 Stream vs StreamReader示例非常棒......两者都是IDisposable,而作为调用者,我正在制作两者,所以我绝不应该给自己一个处理它们中的任何一个的通行证。这样做会违反抽象,将我的调用代码的知识与流类的实现细节联系起来(这可能会随着时间的推移而在学术上发生变化)。
最简单的答案是使用两个usings(),即使你然后去制作(和文档!)第二个IDisposable将处理第一个。