Исключение глотания, выброшенное в блоке catch / finally - PullRequest
10 голосов
/ 31 октября 2009

Обычно я сталкиваюсь с ситуациями, когда мне приходится проглатывать исключение, выданное кодом очистки в блоке 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 ]

23 голосов
/ 31 октября 2009

Я не фанат ловить и отбрасывать исключение.

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

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

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

7 голосов
/ 31 октября 2009

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

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

    throw;
}
4 голосов
/ 31 октября 2009

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

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

1 голос
/ 31 октября 2009

Нет причин откатывать транзакцию в коде 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 гарантирует (через finally), что соединение закрывается независимо от каких-либо исключений, и позволяет исходному исключению всплыть (с полной / правильной трассировкой стека). Если перед вызовом Commit возникает исключение, транзакция никогда не будет зафиксирована и будет автоматически откатываться при закрытии транзакции / соединения.

0 голосов
/ 31 октября 2009

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

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

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

Другим вариантом может быть команда try .. catch .. finally.

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

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

0 голосов
/ 31 октября 2009

Я бы подумал переписать пример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() будет вызван дважды.

0 голосов
/ 31 октября 2009

обычно рискованные коды помещаются все в один блок 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..
}
0 голосов
/ 31 октября 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...