How to handle exception thrown from Dispose?

空扰寡人 提交于 2019-11-30 17:30:37

Its true that it can be pretty bad to leak out an exception of your dispose method, especially since stuff that implements IDisposable will usually specify a finalizer that will call Dispose.

The problem is that sweeping the problem under of the carpet by handling an exception may leave you with some very hard-to-debug situations. What if your IDisposable allocated a critical section of sorts that only gets released after dispose. If you ignore the fact that the exception happened, you may end up in deadlock central. I think failures in Dispose should be one of those cases where you want to fail early, so you can fix the bug as soon as its discovered.

Of course it all depends on the object being disposed, for some objects you may be able to recover, others not. As a general rule of thumb, Dispose should not throw exceptions when used correctly and you should not have to code defensively around exceptions in nested Dispose methods you are calling.

Do you really do not want to sweep an OutOfMemoryException under the carpet?

If I had a dodgy 3rd party component that arbitrarily threw exceptions on Dispose I would get it fixed AND host it in a separate process that I could tear down when it started playing up.

If Dispose() is called inside of a finalization context and it throws an exception, your process will be terminated.

If you suspect that Foo.Dispose() is throwing an exception, I would dispose of it last if possible, and wrap it in try/catch. Do everything you can to get rid of it in the catch - set the reference to null. It is very bad to throw exceptions from Dispose() and should be avoided.

Unfortunately, if this is buggy third-party code, your best bet would be to get them to fix it. You shouldn't have to manually clean up after it.

Hope that helps.

yzorg

Because you don't have to allocate the variables in a using() statement - why not use 'stacked' using statements for this?

void Dispose()
{
    // the example in the question didn't use the full protected Dispose(bool) pattern
    // but most code should have if (!disposed) { if (disposing) { ...

    using (m_foo)
    using (m_bar)  
    {
        // no work, using statements will check null 
        // and call Dispose() on each object
    }

    m_bar = null;
    m_foo = null;
}

The 'stacked' using statements are expanded like this:

using (m_foo)
{
    using (m_bar) { /* do nothing but call Dispose */ }
}

So the Dispose() calls are put in seperate finally blocks:

try {
    try { // do nothing but call Dispose
    }
    finally { 
        if (m_bar != null)
            m_bar.Dispose(); 
    }
finally { 
    if (m_foo != null)
        m_foo.Dispose();
}

I had a hard time finding a reference for this in one place. The 'stacked' using statements are found in an old Joe Duffy blog post (see section 'C# and VB Using Statement, C++ Stack Semantics'). The Joe Duffy post is referenced by many StackOverflow answers on IDisposable. I also found a recent question where stacked using statements for local variables appear to be common. I couldn't find the chaining of finally blocks anywhere but the C# language spec (section 8.13 in C# 3.0 spec), and only for multiple varaibles within a single 'using' block, which isn't exactly what I'm proposing, but if you disasemble the IL you'll find the try/finally blocks are nested. On the null check, also from the C# spec: 'If a null resource is acquired, then no call to Dispose is made, and no exception is thrown.'

Dispose is not supposed to throw any exceptions. If it does—it’s not well written, so…

try { some.Dispose(); } catch {}

should be enough.

According to Design Rules :

"A IDisposable.Dispose method should not throw an exception."

So if your program crashes because of unhandled exception from Dispose() - refer Official Solution

To avoid repeating code for disposing objects, I wrote the following static method.

    public static void DisposeObject<T>(ref T objectToDispose) where T : class
    {
        IDisposable disposable = objectToDispose as IDisposable;
        if (disposable == null) return;

        disposable.Dispose();
        objectToDispose = null;
    }

The main point is that you can make it a function, so you type only one line per object you want to dispose, keeping the Dispose methods nice and clean. In our case, it was convention to null disposed pointers, hence the ref parameters.

In your case, you might want to add exception handling, or make a different flavor with exception handling. I would make sure that you log/breakpoint whenever a Dispose() throws exceptions, but if you can't prevent it, the next best thing is to make sure the problem does not spread.

In my case it was because of a thread accessing UI element while closing the form. I resovled it by aborting the thread on form close. ("FormClosing" event)

FormClosing += (o, e) => worker.Abort();
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!