代码之家  ›  专栏  ›  技术社区  ›  Morgan Cheng

如何处理Dispose抛出的异常?

  •  17
  • Morgan Cheng  · 技术社区  · 16 年前

    最近,我在研究一些关于未处理对象的棘手错误。

    我在代码中找到了一些模式。据报道,一些mufoo没有被释放,而SomeClass的所有实例似乎都被释放了。

    public class SomeClass: IDisposable
    {
        void Dispose()
        {
           if (m_foo != null)
           {
              m_foo.Dispose();
           }
           if (m_bar != null)
           {
              m_bar.Dispose();
           }   
        }
    
        private Foo m_foo;
    
        private Bar m_bar;
    
    }
    

    我怀疑食品处理可能会引发异常,因此不会执行以下代码,因此不会释放mïu bar。

    因为Foo/Bar可能来自第三方,所以不能保证不会抛出异常。

    如果只是用try-catch包装所有Dispose调用,代码将变得笨拙。

    处理这个问题的最佳做法是什么?

    7 回复  |  直到 16 年前
        1
  •  31
  •   Sam Saffron James Allen    16 年前

    确实,泄露dispose方法的异常可能非常糟糕,特别是因为实现IDisposable的东西通常会指定一个finalizer来调用dispose。

    很难调试的情况

    你真的不想把OutOfMemoryException隐藏起来吗?

    如果我有一个狡猾的第三方组件,它在Dispose上任意抛出异常,我会将其修复并将其托管在一个单独的进程中,当它开始运行时,我可以将其拆掉。

        2
  •  4
  •   womp    16 年前

    如果你怀疑食品处理()引发异常,如果可能的话,我会最后处理它,并将其包装在try/catch中。尽一切可能在catch中除去它-将引用设置为null。从Dispose()抛出异常是非常糟糕的,应该避免。

    不幸的是,如果这是有缺陷的第三方代码,最好的办法就是让他们修复它。你不应该在它之后手动清理。

        3
  •  2
  •   noonex    9 年前

    Design Rules :

    “一个IDisposable.处置方法不应引发异常。“

    Official Solution

        4
  •  1
  •   Community CDub    8 年前

    因为您不必在using()语句中分配变量—为什么不为此使用“stacked”using语句呢?

    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;
    }
    

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

    因此Dispose()调用放在单独的finally块中:

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

    old Joe Duffy blog post (参见C1和VB部分,使用语句、C++堆栈语义)。在IDisposable上,Joe Duffy的帖子被许多StackOverflow答案引用。我还找到了一个 recent question 对于局部变量使用语句似乎很常见。我找不到最后几个街区的锁链 C# language spec (C#3.0规范中的第8.13节),并且仅适用于单个“using”块中的多个变量,这与我的建议不完全相同,但是如果您分解IL,您会发现try/finally块是嵌套的。在null检查中,同样来自C规范:“如果获取了null资源,则不会调用Dispose,也不会引发异常。”

        5
  •  1
  •   LukeSw    13 年前

    Dispose不应该抛出任何异常。如果它写得不好,那么

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

        6
  •  0
  •   user180326 user180326    15 年前

    为了避免重复处理对象的代码,我编写了以下静态方法。

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

    主要的一点是,你可以使它成为一个函数,所以你只能键入 每个对象一行

    在您的情况下,您可能需要添加异常处理,或者使用异常处理创建不同的风格。我会确保您在Dispose()抛出异常时记录/断点,但是如果您无法阻止它,那么下一个最好的方法就是确保问题不会扩散。

        7
  •  0
  •   Reza Taibur    8 年前

    在我的例子中,这是因为一个线程在关闭窗体时访问UI元素。我通过中止窗体close上的线程来解决它。(“FormClosing”事件)

    FormClosing += (o, e) => worker.Abort();