在某些情况下,我如何处理类似于以下类的所有异常?
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
}
我也尝试使用“using”但它不处理从~Test()抛出的异常;
static void Main() {
try {
using (Test t = new Test()) { }
}
catch (Exception ex) {
Console.Error.WriteLine(ex.Message);
}
}
任何想法我该如何解决?
答案 0 :(得分:5)
首先,终结者应该从不抛出异常。如果确实如此,那么事情就会发生灾难性的错误,应用程序应该会崩溃。 Finalizer也不应该直接调用Dispose()。终结器仅用于释放非托管资源,因为一旦Finalizer运行,托管资源甚至可能不处于有效状态。垃圾收集器已经清理了托管引用,因此您只需要在Dispose中处理它们,而不是在Finalizer中。
也就是说,如果你明确地调用Dispose,就应该捕获Dispose in Expose。我对“使用”案例没有抛出异常没有很好的理解。也就是说,如果可以避免异常,Dispose也不应该抛出异常。特别是,在使用块之后抛出异常的Dispose将“覆盖”带有Dispose异常的using块内可能发生的任何异常。
一些其他参考资料here
答案 1 :(得分:3)
我认为部分答案是你不应该在这些情况下处理异常。
如果您可以从中恢复,或者您可以向异常添加其他信息并重新抛出,则应该只捕获异常。你不应该抓住每一个例外。让调用堆栈中的代码处理尽可能多的例外。
答案 2 :(得分:1)
我有几点意见。
首先,避免从Dispose中抛出异常。事实上,我几乎会说永远不会。 .NET开发人员已经习惯于期望Dispose将永远成功并且有充分的理由。每次在try-catch中包含调用都会很尴尬,这肯定会降低可读性。
其次,这是一个经常辩论的问题,避免抛出构造函数的异常。与状态验证相关的异常如ArgumentException或IndexOutOfRangeException是可以的,因为它们通常是因为违反前置条件而生成的,并且通常是编程错误的结果。但是,诸如SqlException之类的不可预测的异常几乎会迫使调用者将构造函数包装在try-catch块中。同样,这导致了笨拙的编码方案。但是,更重要的是,在构造函数分配非托管资源然后在将新实例返回给调用者之前抛出异常的情况下,这会导致细微的资源泄漏。如果没有实例引用,调用者就无法调用Dispose。