В чем разница между
try { }
catch
{ throw; }
и
try { }
catch(Exception e)
{ throw e;}
?
И когда я должен использовать один или другой?
В чем разница между
try { }
catch
{ throw; }
и
try { }
catch(Exception e)
{ throw e;}
?
И когда я должен использовать один или другой?
Конструкции
try { ... }
catch () { ... } /* You can even omit the () here */
try { ... }
catch (Exception e) { ... }
аналогичны тем, что оба будут улавливать каждое исключение, созданное внутри блока try
(и, если вы просто не используете это для регистрации исключений, следует избегать). Теперь посмотрим на них:
try { ... }
catch ()
{
/* ... */
throw;
}
try { ... }
catch (Exception e)
{
/* ... */
throw;
}
try { ... }
catch (Exception e)
{
/* ... */
throw e;
}
Первый и второй блоки try-catch ТОЧНО одно и то же, они просто реконструируют текущее исключение, и это исключение сохранит его "источник" и трассировку стека.
Третий блок try-catch отличается. Когда он выдает исключение, он изменит трассировку источника и стека, так что появится, что исключение было выбрано из этого метода из этой самой строки throw e
в методе, содержащем этот блок try-catch.
Какую пользу вы должны использовать? Это действительно зависит от каждого случая.
Скажем, у вас есть класс Person
с методом .Save()
, который будет сохраняться в базе данных. Скажем, что ваше приложение каким-то образом выполняет метод Person.Save()
. Если ваша БД отказывается сохранять Личность, то .Save()
выдаст исключение. Если вы используете throw
или throw e
в этом случае? Ну, это зависит.
Я предпочитаю делать:
try {
/* ... */
person.Save();
}
catch(DBException e) {
throw new InvalidPersonException(
"The person has an invalid state and could not be saved!",
e);
}
Это должно помещать DBException как "Внутреннее исключение" для нового исключения. Поэтому, когда вы проверяете это InvalidPersonException, трассировка стека будет содержать информацию обратно к методу Save (этого может быть достаточно для решения проблемы), но вы все еще имеете доступ к исходному исключению, если вам это нужно.
В качестве заключительного замечания, когда вы ожидаете исключения, вы должны действительно поймать это конкретное исключение, а не общее Exception
, то есть, если вы ожидаете исключение InvalidPersonException, которое вы должны предпочесть:
try { ... }
catch (InvalidPersonException e) { ... }
к
try { ... }
catch (Exception e) { ... }
Удачи!
Первая сохраняет трассировку стека, а вторая сбрасывает ее. Это означает, что если вы используете второй подход, трассировка стека исключения всегда будет начинаться с этого метода, и вы потеряете исходную трассировку исключения, которая может быть катастрофической для тех, кто читает журналы исключений, поскольку он никогда не узнает исходную причину исключения.
Второй подход может быть полезен, если вы хотите добавить дополнительную информацию в трассировку стека, но он используется следующим образом:
try
{
// do something
}
catch (Exception ex)
{
throw new Exception("Additional information...", ex);
}
Там блог post обсуждает различия.
Вы должны использовать
try { }
catch(Exception e)
{ throw }
если вы хотите сделать что-то с исключением перед повторным броском (например, ведение журнала). Одиночный бросок сохраняет трассировку стека.
Разница между беззаметным уловом и catch(Exception e)
заключается в том, что вы получаете ссылку на исключение. Из фреймворка версии 2 неуправляемые исключения обернуты в управляемое исключение, поэтому исключение без параметров больше не полезно ни для чего.
Разница между throw;
и throw e;
заключается в том, что первый используется для повторного исключения исключений, а второй используется для создания вновь созданного исключения. Если вы используете второй вариант для восстановления исключения, он будет рассматривать его как новое исключение и заменяет всю информацию о стеке, откуда она была изначально выбрана.
Итак, вы не использовали ни один из альтернатив в вопросе. Вы не должны использовать захват без параметров, и вы должны использовать throw;
для восстановления исключения.
Кроме того, в большинстве случаев вам следует использовать более конкретный класс исключений, чем базовый класс для всех исключений. Вы должны улавливать только те исключения, которые вы ожидаете.
try {
...
} catch (IOException e) {
...
throw;
}
Если вы хотите добавить какую-либо информацию при повторном создании исключения, вы создаете новое исключение с исходным исключением как внутреннее исключение для сохранения всей информации:
try {
...
} catch (IOException e) {
...
throw new ApplicationException("Some informative error message", e);
}