简体   繁体   English

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

[英]Swallowing exception thrown in catch/finally block

Usually I come across situations where I have to swallow an exception thrown by the clean up code in the catch / finally block to prevent the original exception being swallowed.通常我会遇到这样的情况,我必须吞下catch / finally块中的清理代码抛出的异常,以防止原始异常被吞下。

For example:例如:

// 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;
        }
    }
}

Let's assumed that logging any of the exceptions is not an option in the scope of method block, but will be done by the code calling the example1() and example2() methods.假设记录任何异常不是方法块范围内的选项,而是由调用example1()example2()方法的代码完成。

Is swallowing the exceptions thrown by close() and Rollback() methods a good idea?吞下close()Rollback()方法抛出的异常是个好主意吗? If not, what is a better way of handling the above situations so that the exceptions are not swallowed?如果不是,那么处理上述情况的更好方法是什么,以便不吞下异常?

I'm not a fan of catching and rethrowing an exception.我不喜欢捕获和重新抛出异常。

If you catch it, do something with it - even if it's just logging the exception.如果你抓住了它,就用它做点什么——即使它只是记录了异常。

If you can't do anything with it, don't catch it - add a throws clause to the method signature.如果你不能用它做任何事情,不要抓住它 - 在方法签名中添加一个 throws 子句。

Catching an exception tells me that either you can deal with an exceptional situation and have a recovery plan or "the buck stops here" because an exception cannot propagate in that form any farther (eg, no stack traces back to the user).捕获异常告诉我,您可以处理异常情况并制定恢复计划,或者“责任到此为止”,因为异常无法以这种形式传播得更远(例如,没有堆栈跟踪回用户)。

You can create a custom Exception type that can hold both exceptions.您可以创建一个自定义Exception类型,该类型可以包含这两个异常。 If you overload the ToString() , you can log both exceptions.如果重载ToString() ,则可以记录这两个异常。

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

    throw;
}

That's exactly why Commons IO has an IOUtils.closeQuietly method.这正是 Commons IO 拥有IOUtils.closeQuietly方法的原因。 In most cases what goes wrong during the closing of a file is not that interesting.在大多数情况下,关闭文件期间出现的问题并不那么有趣。

Database transactions that have to be rolled back are potentially more interesting, as in that case the function didn't do what it was supposed to do (put stuff in the DB).必须回滚的数据库事务可能更有趣,因为在这种情况下,函数没有做它应该做的事情(把东西放在数据库中)。

There's no reason to rollback the transaction in the C# code.没有理由在 C# 代码中回滚事务。 If you close the connection wihtout rolling back the transaction (or committing it) that is equivalent and more efficient...如果您在不回滚(或提交)事务的情况下关闭连接,则等效且更有效...

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();
  }
}

and you are done.你就完成了。

The using statement will ensure (via finally) that the connection is closed regardless of any exceptions and let the raw exception bubble out (with the full/correct stack trace). using 语句将确保(通过 finally)无论有任何异常都关闭连接并让原始异常冒泡(带有完整/正确的堆栈跟踪)。 If an exception occurs before the call to Commit, the transaction will never commit and will be automatically rolled back when the transaction/connection closes.如果在调用 Commit 之前发生异常,则事务将永远不会提交,并且会在事务/连接关闭时自动回滚。

I believe that an exception should be something you don't expect.我相信异常应该是你意想不到的。 If you expect an exception then you should do something with it.如果您期望异常,那么您应该对它做些什么。 So in your first example I think you should probably not bother catching the IOException if you are also stating that your method is going to throw IOException.因此,在您的第一个示例中,如果您还声明您的方法将抛出 IOException,则我认为您可能不应该费心去捕获 IOException。

I would consider rewriting example1 as follows:我会考虑如下重写 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.
            }
        }
    }
}

Moving the success = true;移动success = true; to after the out.close();out.close(); statement would make the meaning of success a bit clearer ... though it may result in out.close() being called twice.语句会使success的含义更加清晰......尽管它可能导致out.close()被调用两次。

Without knowing all of what your particular circumstance is, you could look into throwing a new exception.在不了解您的所有特定情况的情况下,您可以考虑抛出一个新的异常。 In C#, at least, when throwing a new exception you one of the optional constructors accepts an existing exception as a parameter.至少在 C# 中,当抛出新异常时,可选构造函数之一接受现有异常作为参数。 For example:例如:

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

The purpose of this is to retain the original exception.这样做的目的是保留原始异常。

Another option might be the try .. catch .. finally construct.另一种选择可能是 try .. catch .. finally 构造。

try { // normal code that might throw an exception } catch (Exception ex) { // handle that first exception } finally { // handle any cleanup regardless of exception being thrown } try { // 可能抛出异常的普通代码 } catch (Exception ex) { // 处理第一个异常 } finally { // 处理任何清理,无论抛出异常如何 }

In general if my code can handle an exception in a particular try .. catch then I don't re-throw that exception.一般来说,如果我的代码可以处理特定 try .. catch 中的异常,那么我不会重新抛出该异常。 If it is important for something further up the call stack to have that exception I will throw a new exception and set the original exception as the inner exception.如果调用堆栈上的某些内容具有该异常很重要,我将抛出一个新异常并将原始异常设置为内部异常。

usually, risky codes are put all in one try-catch block.通常情况下,风险代码放在一个try-catch块。 nested try-catch blocks are not a very good idea, IMO (or just try to avoid nested try-catch blocks as much as possible, unless you really need them).嵌套的 try-catch 块不是一个很好的主意,IMO(或者只是尽量避免嵌套的 try-catch 块,除非您确实需要它们)。

because the risky codes are exceptional scenario, so putting exceptional codes for exceptional case for even more exceptional case, that's alot of unnecessary work.因为有风险的代码是特殊情况,所以把特殊情况的特殊代码放在更特殊的情况下,这是很多不必要的工作。

for example in example1() , place all risky codes in one try-catch block:例如在example1() ,将所有有风险的代码放在一个 try-catch 块中:

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

OR, another good idea is to place several catch(s) for the same try:或者,另一个好主意是为同一次尝试放置多个捕获:

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..
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在finally块中引发异常 - Get thrown exception in finally block 在C#中传播在finally块中抛出的异常而不丢失catch块中的异常的最佳实践是什么? - What is the best practice in C# to propagate an exception thrown in a finally block without losing an exception from a catch block? 在C#中,如果关联的catch块引发异常,是否可以强制控制通过finally块? - in C# is it possible to force control to pass through a finally block if an exception is thrown by an associated catch block? Try catch finally: 如果没有抛出异常,就做点什么 - Try catch finally: Do something if no exception is thrown C#异常处理最后在catch块之前阻塞 - C# exception handling finally block before catch block 在finally块中抛出异常后,返回值会发生什么? - What happens to the returned value after exception is thrown in finally block? 为什么这个catch块没有捕获抛出的异常? - Why is this catch block not catching the thrown exception? 确定是否由于抛出异常而在finally块中执行 - Determine if executing in finally block due to exception being thrown 为什么抛出异常时finally块可能不执行? - Why finally block may not execute when exception is thrown? 如果从 catch 块中抛出异常,什么时候最终运行? - When is finally run if you throw an exception from the catch block?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM