在catch / finally块中抛出吞咽异常

时间:2009-10-31 14:13:00

标签: c# java

通常我会遇到这样的情况:我必须吞下catch / finally块中清理代码抛出的异常,以防止吞下原始异常。

例如:

// Closing a file in Java
public void example1() throws IOException {
    boolean exceptionThrown = false;
    FileWriter out = new FileWriter(“test.txt”);
    try {
        out.write(“example”);
    } catch (IOException ex) {
        exceptionThrown = true;
        throw ex;
    } finally {
        try {
            out.close();
        } catch (IOException ex) {
            if (!exceptionThrown) throw ex;
            // Else, swallow the exception thrown by the close() method
            // to prevent the original being swallowed.
        }
    }
}

// Rolling back a transaction in .Net
public void example2() {
    using (SqlConnection connection = new SqlConnection(this.connectionString)) {
        SqlCommand command = connection.CreateCommand();
        SqlTransaction transaction = command.BeginTransaction();
        try {
            // Execute some database statements.
            transaction.Commit();
        } catch {
            try {
                transaction.Rollback();
            } catch {
                // Swallow the exception thrown by the Rollback() method
                // to prevent the original being swallowed.
            }
            throw;
        }
    }
}

我们假设记录任何异常都不是方法块范围内的选项,而是由调用example1()example2()方法的代码完成。

吞下close()Rollback()方法引发的异常是一个好主意吗?如果没有,那么处理上述情况的更好方法是什么,以便不会吞下异常?

8 个答案:

答案 0 :(得分:23)

我不喜欢捕捉和重新抛出异常。

如果你抓住它,用它做某事 - 即使它只是记录异常。

如果您无法对其执行任何操作,请不要捕获它 - 在方法签名中添加throws子句。

捕获异常告诉我,您可以处理异常情况并制定恢复计划或“降压停止此处”,因为异常无法以该形式传播任何更远的位置(例如,没有堆栈跟踪回溯给用户) 。

答案 1 :(得分:7)

您可以创建一个可以同时包含两个异常的自定义异常类型。如果重载ToString(),则可以记录两个异常。

try
{
    transaction.Commit();
}
catch(Exception initialException)
{
    try
    {
        transaction.Rollback();
    }
    catch(Exception rollbackException)
    {
        throw new RollbackException(initialException, rollbackException);
    }

    throw;
}

答案 2 :(得分:4)

这正是Commons IO采用IOUtils.closeQuietly方法的原因。在大多数情况下,关闭文件时出错的地方并不那么有趣。

必须回滚的数据库事务可能更有趣,因为在这种情况下,该函数没有按预期执行(将内容放入数据库中)。

答案 3 :(得分:1)

没有理由在C#代码中回滚事务。如果您关闭连接而不回滚事务(或提交它),这是相同且更有效的......

public void example2() {
  using (SqlConnection connection = new SqlConnection(this.connectionString))
  using (SqlCommand command = connection.CreateCommand())
  using (SqlTransaction transaction = command.BeginTransaction()) {
    // Execute some database statements.
    transaction.Commit();
  }
}

你已经完成了。

using语句将确保(最后通过)连接被关闭而不管任何异常,并让原始异常冒出来(使用完整/正确的堆栈跟踪)。如果在调用Commit之前发生异常,则事务将永远不会提交,并且在事务/连接关闭时将自动回滚。

答案 4 :(得分:0)

我认为异常应该是你不期望的。如果你期望一个例外,那么你应该对它做点什么。因此,在您的第一个示例中,我认为如果您还声明您的方法将抛出IOException,您可能不应该费心去捕获IOException。

答案 5 :(得分:0)

我会考虑重写example1如下:

// Closing a file in Java
public void example1() throws IOException {
    boolean success = false;
    FileWriter out = new FileWriter(“test.txt”);
    try {
        out.write(“example”);
        success = true;
        out.close();
    } finally {
        if (!success) {
            try {
                out.close();
            } catch (IOException ex) {
                // log and swallow.
            }
        }
    }
}

success = true;移到out.close();语句后会使success的含义更清晰......但可能会导致out.close()被调用两次。< / p>

答案 6 :(得分:0)

如果不了解您的特定情况,您可以考虑抛出新的异常。至少在C#中,当抛出一个新的异常时,其中一个可选的构造函数接受一个现有的异常作为参数。例如:

throw new Exception("This is my new exception.", ex);

这样做的目的是保留原始例外。

另一个选择可能是try .. catch .. finally constru。

尝试{     //可能引发异常的普通代码   } catch(Exception ex){     //处理第一个异常   } finally {     //处理任何清理,无论抛出异常   }

一般情况下,如果我的代码可以在特定的try .. catch中处理异常,那么我不会重新抛出该异常。如果调用堆栈中的某些内容对于该异常很重要,我将抛出一个新异常并将原始异常设置为内部异常。

答案 7 :(得分:-2)

通常,有风险的代码会在一个try-catch块中加上 all 。嵌套的try-catch块不是一个好主意,IMO(或者尽量避免嵌套的try-catch块,除非你真的需要它们。)

因为风险代码是特殊情况,所以为特殊情况下的特殊代码提供了更特殊的情况,这是很多不必要的工作。

例如在example1()中,将所有有风险的代码放在一个try-catch块中:

try{
FileWriter out = new FileWriter(“test.txt”);
out.write(“example”);
out.close();
} catch(Exception e) {
    //handle exception
}

或者,另一个好主意是为同一次尝试放置几个catch:

try{
    FileWriter out = new FileWriter(“test.txt”);
    out.write(“example”);
    out.close();
} catch(FileNotFoundException e) {
        //if IOException, do this..
} catch(IOException e) {
        //if FileNotFound, do this..
} catch(Exception e) {
        //completely general exception, do this..
}