2

在某些情况下,如何处理类似于以下的类的所有异常?

class Test : IDisposable {
  public Test() {
    throw new Exception("Exception in ctor");  
  }
  public void Dispose() {
    throw new Exception("Exception in Dispose()");
  }
  ~Test() {
    this.Dispose();
  }
}

我试过这个但它不起作用:

static void Main() {
  Test t = null;
  try {
    t = new Test();
  }
  catch (Exception ex) {
    Console.Error.WriteLine(ex.Message);
  }

  // t is still null
}

我也尝试过使用“使用”,但它不处理从 ~Test(); 抛出的异常;

static void Main() {
  try {
    using (Test t = new Test()) { }
  }
  catch (Exception ex) {
    Console.Error.WriteLine(ex.Message);
  }
}

有什么想法可以解决吗?

4

3 回答 3

5

首先,终结器永远不应该抛出异常。如果是这样,则说明发生了灾难性的错误,应用程序应该会严重崩溃。终结器也不应该直接调用 Dispose()。终结器仅用于释放非托管资源,因为一旦终结器运行,托管资源甚至可能不处于有效状态。垃圾收集器已经清理了托管引用,因此您只需在 Dispose 中处理它们,而不是在 Finalizer 中。

也就是说,如果您显式调用 Dispose,则应捕获 Dispose 中的异常。我不太了解“使用”案例如何没有引发异常。也就是说,如果可以避免的话,Dispose 也不应该抛出异常。特别是,在 using 块之后引发异常的 Dispose 将使用 Dispose 异常“覆盖”在 using 块内可能发生的任何异常。


这里有一些额外的参考资料

于 2010-04-20T01:24:12.990 回答
3

我认为部分答案是在这些情况下您不应该处理异常。

如果您可以从异常中恢复,或者您可以向异常添加附加信息并重新抛出,您应该只捕获异常。您不应该捕获所有异常。让调用堆栈中较高的代码处理尽可能多的异常。

于 2010-04-20T01:30:29.223 回答
1

我有几点意见。

首先,避免从 Dispose 抛出异常。事实上,我几乎会说永远不会。.NET 开发人员已经习惯于期望 Dispose 总是会成功并且有充分的理由。每次都将调用包装在 try-catch 中会很尴尬,而且肯定会降低可读性。

其次,这是一个经常争论的问题,避免从构造函数中抛出异常。与状态验证相关的异常,例如 ArgumentException 或 IndexOutOfRangeException 是可以的,因为它们通常是由于违反前置条件而生成的,并且通常是编程错误的结果。但是,诸如 SqlException 之类的不可预测的异常几乎会迫使调用者将构造函数包装在 try-catch 块中。同样,这会导致尴尬的编码场景。但是,更重要的是,在构造函数分配非托管资源然后在将新实例返回给调用者之前抛出异常的情况下,这可能会导致细微的资源泄漏。如果没有实例引用,调用者将无法调用 Dispose。

于 2010-04-20T02:35:01.990 回答