【问题标题】:IDisposable.Dispose is never called after exception in using blockIDisposable.Dispose 在使用块的异常后永远不会被调用
【发布时间】:2015-09-10 15:18:39
【问题描述】:

我从thisthis 等许多来源了解到,如果在Using 块中引发异常,将始终调用IDisposableDispose 方法。所以我有这个代码:

static class MainEntryPoint
{
    static void Main(string[] args)
    {
        AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException;

        using (var x = new Disposable())
        {
            throw new Exception("asdfsdf");
        }
    }

    private static void HandleUnhandledException(Object sender, System.UnhandledExceptionEventArgs e)
    {
        Environment.Exit(0);
    }
}

class Disposable : IDisposable
{
    public void Dispose()
    {
        System.Diagnostics.Debug.Print("I am disposed");
    }
}

当抛出未处理的异常时,它会退出应用程序。 Dispose 方法永远不会被调用。为什么?

【问题讨论】:

    标签: c# .net exception idisposable using-statement


    【解决方案1】:

    Environment.Exit 将终止程序

    如果从 try 或 catch 块调用 Exit,任何 finally 中的代码 块不执行。如果使用return语句,则代码在 finally 块确实执行了。

    using (var x = new Disposable())
    {
        throw new Exception("asdfsdf");
    }
    

    将转换为

    Disposable x = new Disposable();
    try
    {
        throw new Exception("asdfsdf");
    }
    finally
    {
        if (x != null)
            x.Dispose();
    }
    

    【讨论】:

    • 是的,using语句确保即使发生异常也会调用Dispose。
    【解决方案2】:

    没错!由于在事件处理程序中调用了 Environment.Exit(0),因此无法调用 Dispose 方法中的代码。

    尝试移除对 Environment.Exit(0) 的调用,看看是否调用了 Debug.Print()。

    【讨论】:

      【解决方案3】:

      请注意,如果您已将终结器添加到 Disposable,例如:

      public class Disposable : IDisposable
      {
          public void Dispose()
          {
              Dispose(true);
          }
      
          protected virtual void Dispose(bool disposing)
          {
              Console.WriteLine("I am disposed");
      
              if (disposing)
              {
                  GC.SuppressFinalize(this);
              }
          }
      
          ~Disposable()
          {
              Dispose(false);
          }
      }
      

      (因此使用“完整”IDisposable 模式),然后“通常”会调用终结器(因为终结器有机会在Environment.Exit 上运行),并且该方法将调用Dispose(bool disposing)。请注意,即使在这里也有可能不会运行终结器,因为它们有运行时间限制,see

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-10-16
        • 1970-01-01
        • 2012-10-27
        • 2013-10-12
        • 2012-03-27
        • 2013-08-29
        • 2013-11-03
        相关资源
        最近更新 更多