我通常遇到在我的代码中实现IDisposable的需求。要正确处理托管和非托管资源,需要合理数量的样板代码。根据此处找到的文档,我创建了以下基类。

我的意图是,我创建的任何需要处理资源的对象都无需重写样板代码。相反,它可以继承自此对象并实现两个抽象方法。

这看起来正确吗?还有其他人写过类似的东西吗?我希望您对样式,正确性以及它与最佳做法的符合程度发表意见。

/>

评论

我看到的一个问题是,您会很快遇到多个继承问题(或者缺少继承问题)。但是,我经常看到类似的实现,并且在某些情况下,您控制整个继承层次结构,我认为它很有用。

我也认为继承(或缺乏继承)。首先,我使用此类来帮助包装要在托管代码中使用的非托管库。对于这种情况,我决定可以忍受继承的限制。

#1 楼

我会考虑进行两个更改:将一个或两个Dispose*Resources方法设为虚拟而不是抽象。尽管这在很大程度上取决于您需要多长时间处理一次非托管资源。我不记得上次我处理它们时,我讨厌在每个类中重写此方法只是为了使其为空。
我将在您的finalizer方法或Dispose(bool disposing)方法中添加一些日志记录,以捕获通过调用Dispose()方法无法正确处理一次性对象的情况。大多数开发人员都在寻找此类信息,因此您有一个注入这些信息的好地方。


评论


\ $ \ begingroup \ $
都很好的建议,谢谢!就日志记录而言,我正在考虑添加断言语句以警告开发人员未调用Dispose。
\ $ \ endgroup \ $
–那撒奈尔
2011年5月31日下午16:

#2 楼

过去,在实现此模式时,我发现有用的东西是在构造过程中捕获堆栈跟踪,并将其包括在Debug.Assert消息中。

知道构造对象的位置通常可以为您提供帮助跟踪问题的根源,尤其是如果这些对象是在一个地方创建的,然后需要由其他一些对象处理,这些对象将负责其整个生命周期。

#3 楼

我同意Brian的观点,但我相信Dispose(bool disposing)方法应该是抽象的。我这样做的理由是,首先必须有一个从DisposableObject类型继承的原因,在这种情况下,将方法设为virtual似乎没有意义,因为它将是可选实现。如果是可选的,为什么仍要从DisposableObject继承?

在有一些通用代码的情况下将其虚拟化将是有益的,但也许会有更好的设计:

protected virtual void Dispose(bool disposing)
{
  if (!Disposed)
  {
    DisposeInternal(disposing);
    Disposed = true;
  }
}

protected abstract void DisposeInternal(bool disposing);


这样,您可以确保仅在实际处置时才调用DisposeInternal,并且不需要设置Dispose标志,因为该标志由外部调用处理。

评论


\ $ \ begingroup \ $
我对使该方法成为虚拟方法而不是抽象方法的评论特别是针对存在单独的方法来清理托管和非托管资源的情况。在只有一种清除方法的情况下,我同意它应该是抽象的。
\ $ \ endgroup \ $
–布赖恩·赖希尔(Brian Reichle)
2011年6月3日,下午3:29

\ $ \ begingroup \ $
在示例代码中,无论是否进行处置,都应调用DisposeInternal()并将其传递到处置中,因为如果未正确处置非托管资源,则应在最终确定时清除它们。
\ $ \ endgroup \ $
–布赖恩·赖希尔(Brian Reichle)
2011年6月3日,下午3:38

\ $ \ begingroup \ $
@Brian-好吧,我会更新我的答案。
\ $ \ endgroup \ $
–马修·雅培(Matthew Abbott)
2011年6月3日,6:10

\ $ \ begingroup \ $
我不同意此功能应该公开。对于类的用户,永远不会存在调用Dispose(false)正确的情况。提供此区别仅是为了在运行时,如果开发人员忘记在对象上调用Dispose,我们将不会与GC竞争处置任何剩余的对象。
\ $ \ endgroup \ $
–那撒奈尔
2011年6月3日19:01

\ $ \ begingroup \ $
另一个好处是,我将其更改为受保护的...
\ $ \ endgroup \ $
–马修·雅培(Matthew Abbott)
2011年6月3日22:51

#4 楼

就我个人而言,我将使Dispose(bool disposing)受保护是虚拟的,而不是使用2种抽象方法...与框架中大多数未密封的一次性对象保持一致。另外,我还记得FxCop对于IDisposable的实现非常痴迷。 。我发现我的一次性对象处置其他对象比清理自己的非托管资源更为普遍。

#5 楼

如果这仅限于包装非托管库,那么它可能会很有用,但是我永远不会在没有非托管资源的地方使用它,它将完全搞乱垃圾收集器,因为除非有终结器,否则除非一个绝对需要它。

您必须非常仔细地实现终结器;这是一个复杂的操作,可能会带来相当大的性能开销。

评论


\ $ \ begingroup \ $
我甚至建议该实现终结器的DisposableObject类的单独子类。大多数子类都将直接从DisposableObject继承,但是如果您需要为不受管理的对象编写快速包装,则可以使用带有终结器的专用抽象子类。
\ $ \ endgroup \ $
– Binki
2014年7月3日19:11



#6 楼

添加检查和抛出的方法可以清除很多噪音。

protected void VerifyDisposed()
{
    if (_disposed)
    {
        throw new ObjectDisposedException(GetType().FullName);
    }
}