【问题标题】:How to handle exceptions thrown by disposable objects?如何处理一次性对象抛出的异常?
【发布时间】:2012-08-01 09:17:58
【问题描述】:

假设 Constructor 和 Process 方法可能抛出异常,使用 Disposable 对象的最佳方法是什么?我通常更喜欢以下实现之一。

  1. try-catch 使用块包围

    try
    {
        using (Disposable dispObj = new Disposable())
        {
            dispObj.Process();
        }
    }
    catch (Exception ex)
    {
        // Do something
    }
    
  2. try-catch-finally 块。

    Disposable dispObj2 = null;
    try
    {
        dispObj2 = new Disposable();
        dispObj2.Process();
    }
    catch (Exception ex)
    {
        // Do something
    }
    finally
    {
        if (dispObj2 != null)
        {
            dispObj2.Dispose();
        }
    }
    

更新:

再次重申:“假设 Constructor 和 Process 方法可能会抛出异常”。我真的不明白为什么没有人关心他们的答案中的异常。

【问题讨论】:

  • 为什么是空的catch{}
  • 你的班级Disposable 必须实现IDisposable interface,顺便说一句。

标签: c# idisposable


【解决方案1】:

using 很好。它有一个内置的 try-finally 块。如果发生异常,会自动调用 dispose 方法。

这很好

using (Disposable dispObj = new Disposable())
{
    dispObj.Process();
}

【讨论】:

  • 构造函数或进程方法抛出异常怎么办?
  • 那么也会处理。
  • @NikhilAgrawal 需要在评论中引用
【解决方案2】:

这样做:

using (Disposable dispObj = new Disposable())
{
    dispObj.Process();
}

Disposable 对象总是在它们超出 using 子句的范围时被释放,即使它是由异常引起的。

并且不要使用空的catch {},完全没有意义。

【讨论】:

  • 构造函数或进程方法抛出异常怎么办?
  • 这就是using 子句的全部内容。 即使发生异常,它也会始终处理在其中构造的对象。它基本上就像使用 try/finally 一样,除了少写。我什至在这个答案中也这么说。
  • 我知道,但我想了解的是人们如何处理 Disposable 对象抛出的异常。一个 try-catch-finally 或一个 try-catch 使用块或其他东西。
  • 如果你有一个一次性对象并且想以特定方式处理异常,我建议使用 try/catch/finally。在finally - 处理它,在catch - 处理异常。但当然,这在一定程度上取决于您是否可以查明代码中应该发生异常的位置,在这种情况下,您可能希望使用using,然后用 try/catch 包围可能失败的部分。
【解决方案3】:

using 语句在后台使用 try - finally,并且仅适用于实现 IDisposable 的对象。您不必将 try catch 与 using 一起使用。请看以下示例(来自MSDN - using statement)。

using (Font font1 = new Font("Arial", 10.0f)) 
{
    byte charset = font1.GdiCharSet;
}

以上代码同:

Font font1 = new Font("Arial", 10.0f);
  try
  {
    byte charset = font1.GdiCharSet;
  }
  finally
  {
    if (font1 != null)
      ((IDisposable)font1).Dispose();
  }

【讨论】:

  • 构造函数或进程方法抛出异常怎么办?
【解决方案4】:
using (Disposable dispObj = new Disposable())
    {
        dispObj.Process();
    }
}

【讨论】:

  • 构造函数或进程方法抛出异常怎么办?
【解决方案5】:

实现 IDisposable 接口的类通常使用 .NET 框架垃圾收集器无法清理的资源。 调用 IDisposable.Dispose() 方法会执行将宝贵资源显式释放回世界的代码。 实现 IDisposable 接口的类的一个主要示例是 SqlConnection 类。 SqlConnection 类使用 Microsoft SQL Server 数据库连接。由于 SQL Server 支持的连接数量有限,因此尽快释放连接非常重要。 通常,您不会直接调用 Dispose() 方法。通常,您可以像代码中提到的那样利用代码中的 Using 语句

【讨论】:

  • 您还应该实现 IDisposable 以在任何实现 IDisposable 本身的组合对象(无论是在成员字段还是属性中)上调用 Dispose
【解决方案6】:

在可能的情况下,您应该使用 using 块。它将保证在您的对象上调用 Dispose。手动操作很容易出错。

【讨论】:

    【解决方案7】:
    using (IDisposable disp = new IDisposable())
    {
       //some logic here
    }
    

    这段代码将被翻译成这样:

    IDisposable disp = new IDisposable();
    
    try
    {
      //some logic here
    }
    finally
    {
      if (disp != null)
         ((IDisposable)disp).Dispose();
    }
    

    “最终”块中的代码始终执行,因此它使您有可能抛出异常,从 using 块中返回,毫无疑问 Dispose 不会被调用。

    但是你应该小心使用和对象初始化器。

    using (var fs = new FileStream(@"C:\blabla", FileMode.Open) { Position = pos })
    {
        //some logic here
    }
    

    here 属性可以看出,Position 可以抛出异常。问题是对象将被创建并且这将在 在尝试块之外完成,因此可能存在内存泄漏。正确的方法

    using (var fs = new FileStream(@"C:\blabla", FileMode.Open))
    {
        fs.Position = pos;
        //some logic here
    }
    

    【讨论】:

    • 如果“一些逻辑”或“新文件流”方法抛出异常怎么办?
    • 如果“new FileStream”抛出异常,它是“好的”。对象没有被创建,所以没有什么要处理的。如果“某些逻辑”抛出异常,则 dispose 将从 finally 调用。
    • 好的,我知道,但是异常处理呢?这个问题是:您更喜欢使用 try-catch 还是使用 try-catch-finally 或其他方式?
    【解决方案8】:

    您提出的方法 #1 可能是实现您想要的最简洁的方法。

    周围的try...catch 块应该捕获任何抛出的异常,包括构造函数抛出的异常。如果在一次性对象正在使用时(在using 块内)发生异常,则该对象也将在catch 接管之前被处置。

    方法 #2 采用与方法 #1 不同的执行路径。请注意,根据C# Reference,方法#1 的字面意思是编译如下:

    try
    {
        {
            Disposable dispObj = new Disposable();
            try
            {
                dispObj.Process();
            }
            finally
            {
                if (dispObj != null)
                    ((IDisposable)dispObj).Dispose();
            }
        }
    }
    catch (Exception ex)
    {
        // Do something
    }
    

    所以,这两种方法的作用域是不同的。如果可能,我更喜欢方法 #1,因为范围问题,也因为它更容易阅读。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-20
    • 1970-01-01
    • 1970-01-01
    • 2013-07-24
    • 2019-02-21
    • 2022-11-25
    • 2021-12-22
    • 2013-03-05
    相关资源
    最近更新 更多