Заглоченное исключение, заброшенное в catch/finally block

Обычно я сталкиваюсь с ситуациями, когда мне нужно усвоить исключение, созданное очищающим кодом в блоке 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(), хорошая идея? Если нет, то лучший способ справиться с вышеуказанными ситуациями, чтобы исключения не проглатывались?

Ответ 1

Я не поклонник ловить и переубедить исключение.

Если вы поймаете его, сделайте что-нибудь с ним - даже если он просто регистрирует исключение.

Если вы ничего не можете с этим сделать, не поймите его - добавьте предложение throw к сигнатуре метода.

Привлечение исключения говорит мне, что либо вы можете справиться с исключительной ситуацией, либо иметь план восстановления, либо "остановка доллара здесь", потому что исключение не может распространяться в этой форме дальше (например, трассировка стека не возвращается пользователю),

Ответ 2

Вы можете создать настраиваемый тип исключения, который может содержать оба исключения. Если вы перегрузите ToString(), вы можете зарегистрировать оба исключения.

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

    throw;
}

Ответ 3

Именно поэтому у Commons IO есть метод IOUtils.closeQuietly. В большинстве случаев, что не так во время закрытия файла, это не так интересно.

Операции с базами данных, которые нужно откатить, потенциально более интересны, так как в этом случае функция не выполняла то, что она должна была делать (помещала вещи в БД).

Ответ 4

Нет причин отката транзакции в коде С#. Если вы закроете соединение с откатом транзакции (или ее фиксации), которая эквивалентна и более эффективна...

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 возникает исключение, транзакция никогда не будет завершена и будет автоматически откат при закрытии транзакции/соединения.

Ответ 5

Я считаю, что исключение должно быть тем, чего вы не ожидаете. Если вы ожидаете исключения, вы должны что-то с этим сделать. Таким образом, в вашем первом примере я думаю, что вам, вероятно, не стоит беспокоиться об IOException, если вы также заявляете, что ваш метод будет генерировать исключение IOException.

Ответ 6

Я хотел бы переписать пример1 следующим образом:

// 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() дважды.

Ответ 7

Не зная всех своих обстоятельств, вы можете заглянуть в новое исключение. В С#, по крайней мере, при метании нового исключения один из необязательных конструкторов принимает существующее исключение в качестве параметра. Например:

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

Цель этого - сохранить исходное исключение.

Другим вариантом может быть попытка.. catch.. окончательно построить.

попробуйте {   // нормальный код, который может вызывать исключение } catch (Exception ex) {   // обрабатываем это первое исключение } в конце концов {   // обрабатывать любую очистку, независимо от того, выбрано ли исключение }

В общем случае, если мой код может обрабатывать исключение в конкретной попытке.. catch, я не перебрасываю это исключение. Если для чего-то большего, чем в стеке вызовов, есть это исключение, я создам новое исключение и установлю исходное исключение как внутреннее исключение.

Ответ 8

обычно, рискованные коды помещаются all в один блок try-catch. вложенные блоки 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
}

ИЛИ, еще одна хорошая идея - разместить несколько улов (ов) для одной и той же попытки:

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