IDisposable.Dispose()方法的实现是否应该是幂等的?

23

微软.NET框架提供了IDisposable接口,其中要求实现void Dispose()方法。它的目的是允许手动或基于范围的释放可能已分配的昂贵资源。例如,数据库集合、流和句柄。

我的问题是,Dispose()方法的实现是否应该是幂等的 - 当在同一实例上调用多次时,只有一次将其"处理"并且后续调用不会抛出异常。在Java中,大多数具有类似行为的对象(再次以流和数据库连接为例)对于其close()操作是幂等的,这恰好是Dispose()方法的类比。

然而,我个人在.NET(特别是Windows Forms中)的经验表明,并非所有实现(这些实现是.NET框架本身的一部分)都是幂等的,因此对这些的后续调用会抛出ObjectDisposedException。这真的让我对如何处理可处理对象的实现感到困惑。对于这种情况是否有通用答案,还是取决于对象及其使用方面的具体上下文?

4个回答

20

Dispose() 方法的实现是否需要幂等?

是的,需要。无法确定它将被调用多少次。

来自 MSDN 上的 实现 Dispose 方法

Dispose 方法应该可以被多次调用而不会抛出异常。

一个实现了 IDispose 的良好对象将拥有一个布尔型标志字段,指示其是否已经被处理过,对于后续的调用将不做任何处理(因为它已经被处理过了)。


7
微软并不总是遵循自己的建议,但这并不意味着你也应该这么做。 - linkerro
@linkerro - 你能否详细解释一下你那有点神秘的评论吗? - Oded
我想知道他是否指的是如果你两次Dispose一个Control,WinForms会抛出异常。 - DaveShaw
@IvayloSlavov - 每当您在多个线程之间共享状态时... 但您真的应该针对此提出一个新问题。 - Oded
就像Dave所说的那样,我特别谈论winforms句柄释放以及一般情况下容易出错的句柄释放。 - linkerro
显示剩余2条评论

7

是的,还要确保在对象已被处理时调用类的其他方法时,它们能够正确响应。

public void SomeMethod()
{
     if(_disposed)
     {
         throw new ObjectDisposedException();
     }
     else
     {
         // ...
     }

}

在这里应对的方式可能只是抛出异常。而且我并不确定是否需要任何要求。请不要要求已释放的对象以某种方式仍然“工作”。尝试使用已释放的对象是一个错误,应该被视为这样的错误。 - R. Martinho Fernandes
@R.MartinhoFernandes - 是的,但这仍然是需要添加的内容 :) - Emond
@R.MartinhoFernandes,我认为@Emo的意思是 - 如果您仍然拥有对已处理对象的引用,并调用它具有的某些其他方法,则这些方法应引发ObjectDisposedException,否则通知调用者该操作在此状态下是非法的。不幸的是,我认为这可能会以同步意识的代价为代价。 - Ivaylo Slavov
1
我认为抛出一个愚蠢的异常(使用Eric Lippert的分类)是正确的做法。现在答案更加明确了,我甚至点了+1。之前可能会被误解为其他意思。 - R. Martinho Fernandes
1
@R.MartinhoFernandes - 自己记住:如果两行更好,永远不要试图用一行来解释某件事情。 - Emond

4

MSDN上的描述:

允许多次调用Dispose方法而不会抛出异常。在第一次调用之后,该方法不应做任何操作。


3
为了更清晰明确,您可能应该包含下一个要点:“当资源已经被释放时,请从此类型的实例方法(除Dispose之外)抛出ObjectDisposedException。但是此规则不适用于Dispose方法,因为它应该可以被多次调用而不会引发异常。” - Jamiec

4
个人而言 - 是的 - 我总是使Dispose()具有幂等性。
在给定应用程序中对象的通常生命周期中,可能并不需要这样做 - 从创建到处理的生命周期可能是确定性和已知的。
然而,在某些应用程序中可能并不那么清晰。例如,在装饰器场景中:我可能拥有一个可处理对象A,由另一个可处理对象B进行装饰。我可能想显式释放A,但是在B上执行Dispose也可能会释放它包装的实例(例如:流)。
既然使Dispose具有幂等性相对容易(即如果已释放,则不执行任何操作),因此不这样做似乎很愚蠢。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接