在IDisposable类层次结构中正确处理ObjectDisposedException

正确实现IDisposable时,大多数实现(包括框架指南)都建议包含一个private bool disposed; 成员为了安全地允许多次调用Dispose()Dispose(bool)以及在适当时抛出ObjectDisposedException 。

这适用于单个类。 但是,当您从可支配资源中inheritance子类,并且子类包含其自己的本机资源和独特方法时,事情会变得有点棘手。 大多数示例都展示了如何正确地重写Dipose(bool disposing) ,但不要超越它来处理ObjectDisposedException

在这种情况下,我有两个问题。


第一:

子类和基类都需要能够跟踪处置状态。 我知道有几个主要选项 –

  • 1)声明私人布尔处置; 在这两个class级。 每个类跟踪它自己的this.disposed,并根据需要抛出。

  • 2)使用protected bool Disposed {get; 私人集; 而不是一个字段。 这会让子类检查处理状态。

  • 3)提供一些受保护的辅助方法来检查处理状态,如果对象被处理则通过reflection拉动当前类型名称来抛出。

我看到每个选项的优点是:

  • 1)这“气味”给我,因为它包含重复的布尔值,但似乎工作正常。 我经常在子类化其他代码时使用它。

  • 2)这取出了重复的布尔值,但不是设计指南书的编写方式等。这是我通常使用的,因为它使状态保持单点。

  • 3)这对我来说似乎是最干净的选择,但没有出现在标准指南中。 对于一种方法而言,它可能比该类用户的其他方式要少一些。

在某些方面,我尝试过使用这三种方法。 我想知道这三种方法的优点和缺点,以及任何其他想法,以更清洁,更好的方式来处理这个问题。 你在处理这个问题时会做出什么选择?为什么?


第二:

抛出ObjectDisposedException ,您对name参数使用了什么? 我知道“典型”的方法调用是:

 throw new ObjectDisposedException(GetType().FullName); 

Microsoft员工在此页面上发表评论,建议实施具体类的全名是适当的用法。

在上面的第三个选项中,这将是唯一有意义的选择。 但是,如果类实现了throw本身,则可能会返回定义所调用方法的类的名称。 (即:基类可以返回基类的名称,而不是具体的子类)

我认为这不是一个好主意 – 但我在其他人编写的代码上遇到了这个问题。 使用实现方法的类的名称返回是有优点还是缺点?

我通常实现第一个选项。 实际上,它似乎是设计指南推荐的内容。 原因并不是很明显,但我认为它是一个很好的原因:该类的任何实现者应该具有相同的保护,以防止对象作为一般消费者处置。 换句话说,最好不要假设实现派生类的人确切地知道何时可以调用某个方法,该方法的成功执行可能会或可能不会取决于该对象是否已被处置(尽管理想情况下这应该是无论如何都通过XML注释记录)。

关于你的第二个问题,我将再次坚持传递GetType().FullName的推荐做法,特别是因为它在核心.NET框架中使用。 即使你认为替代方法更合适,我认为为了保持一致性,最好坚持使用.NET框架中使用的方法。

总结:与所有指南一样,显然由您决定如何实现某个设计function,但除非您有特别好的理由,否则坚持使用它们是非常明智的。 在这两种情况下,使用您在post中建议的一些替代方案可能不会造成很大的伤害,只要它们被一致地使用并且最好记录给用户。

声明私人布尔处置; 在这两个class级。 每个类跟踪它自己的this.disposed,并根据需要抛出。

当您无法修改基类时,这是实用的解决方案。

使用protected bool Disposed {get; 私人集; 而不是一个字段。 这会让子类检查处理状态。

为什么不公开并称之为IsDisposed呢? 然后你会做与System.Windows.Forms.Control相同的事情。 当您可以修改基类时,这是一个很好的解决方案。

您可能会返回定义该方法的类的名称

不是。您引用的示例代码使用了“GetType()。FullName”。 这始终是派生类型最多的名称,而不是实现特定方法的类型。

我倾向于让基类声明一个整数IsDisposed标志,然后让外部的“Dispose()”方法Interlocked。在开始处理之前将它更改为“1”(如果在交换之前它不是零,则跳过处理)。 支持Finalize需要更复杂一些,但实现Finalize的类不应该从Object以外的类派生,并且 – 除了SafeHandle之类的专用或实用程序类 – 不应该inheritance。