在终结器中抛出异常以强制执行Dispose调用:

时间:2013-12-03 18:02:46

标签: c# .net dispose idisposable

以下是我认为推荐的典型IDisposable实现:

~SomeClass() {
    Dispose(false);
}

public void Dispose() {
    GC.SuppressFinalize(this);
    Dispose(true);
}

protected virtual void Dispose(bool isDisposing) {
    if(isDisposing) {
        // Dispose managed resources that implement IDisposable.
    }
    // Release unmanaged resources.
}

现在,根据我的理解,终结器背后的想法是,如果我不调用Dispose,我的非托管资源仍将正常发布。但是,据我所知,人们普遍认为不在实现IDisposable的对象上调用Dispose可能是一个bug。

有没有特别的理由不完全接受这个而是这样做?

~SomeClass() {
    throw new NotImplementedException("Dispose should always be called on this object.");
}

public virtual void Dispose() {
    GC.SuppressFinalize(this);

    // Dispose managed resources that implement IDisposable.

    // Release unmanaged resources.
}

4 个答案:

答案 0 :(得分:8)

从.NET 2.0及更高版本开始,如果未覆盖默认策略,则会在Finalizer causes the process to terminate中抛出未处理的异常。

据我了解,Finalizer不是预期位置,应该抛出异常。我认为,如果Finalizer正确执行,可能会因为意外原因(线程中止,......)而无法调用Dispose()方法进行干净恢复。

答案 1 :(得分:3)

在终结器中抛出异常几乎总是一个坏主意。

即使可能存在实际上会达到预期结果的情况(导致程序员在没有弄乱任何其他事情的情况下被通知问题),终结器将无法知道何时会出现问题案件

更好的方法是使用一种方法来指示(可能通过抛出任何异常)是否有任何对象被错误地抛弃;可以在程序结束时调用该方法,也可以在创建新对象时调用该方法。这可能会导致异常被抛入代码的一部分,这与被放弃的特定实例无关,但这仍然可能比在终结器上下文中抛出异常更好。

答案 2 :(得分:2)

从终结器中抛出异常是您可能做过的最糟糕的事情之一。它可能会导致几种不同的行为。异常可能导致ExecutionEngine崩溃从而导致整个进程崩溃,您可能会阻止终结器导致OutOfMemory(OOM)崩溃等。不要忘记终结器在单个线程上运行并且是最重要的线程之一 - 你不希望它搞砸了。

答案 3 :(得分:0)

由于开发人员倾向于变得懒惰而忽略任何可以忽略的警告,而且IIS7.5应用程序默认完全忽略Debug.Fail我也使用相同的模式。有一个例外:

我只在调试版中执行此操作,因为它对发布版本没有任何好处。此外,它从不直接保存非托管资源的类中删除了慢速终结器。

public void Dispose()
{
    // Do your dispose stuff here
#if DEBUG
    GC.SuppressFinalize(this);
}
~MyClass()
{   throw new ObjectNotDisposedException(); // This class is not intended to be used without Dispose.
#endif
}

有些情况下,丢失的处理可能真的会占用应用程序,例如:远程事务上下文。在这种情况下,终止并不会更糟,并且会让懒惰的开发人员感到紧张。我总是喜欢把问题带回来。