Разница между try / catch / throw и try / catch (e) / throw e

103

В чем разница между

try { }
catch
{ throw; }

и

try { }
catch(Exception e)
{ throw e;}

?

И когда я должен использовать тот или другой?

Карим
источник

Ответы:

151

Конструкции

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

Удачи!

Бруно Рейс
источник
34

Первый сохраняет трассировку стека, а второй сбрасывает ее. Это означает, что если вы используете второй подход, трассировка стека исключения всегда будет начинаться с этого метода, и вы потеряете исходную трассировку исключения, что может иметь катастрофические последствия для читателя журналов исключений, поскольку он никогда не узнает исходную причину исключения. .

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

try
{
    // do something
}
catch (Exception ex)
{
    throw new Exception("Additional information...", ex);
}

Там в блоге после обсуждения различий.

Дарин Димитров
источник
Что ж, это здорово!
Майлз
так зачем тогда использовать второй? лучше использовать только первый?
Карим
1
Второй пригодится, когда вам нужно проверить наличие определенных исключений - на ум приходит OutOfRangeException - или вам нужно записать сообщение и т. Д. Первый, похоже, обработчик исключений с подстановочными знаками, похожий на try {} catch (...) {} в c ++.
3ave
1
Дэвид, это относится только к части улова (исключение e) . И это отдельно от throwпротив throw e.
Хенк Холтерман
6

Вы должны использовать

try { }
catch(Exception e)
{ throw }

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

Отавио Десио
источник
и что будет, если я заменю здесь "throw" на "throw e"?
Карим
5

Разница между catch без параметров и a в catch(Exception e)том, что вы получаете ссылку на исключение. Начиная с версии платформы 2, неуправляемые исключения заключены в управляемое исключение, поэтому исключение без параметров больше ни для чего не используется.

Разница между throw;и throw e;заключается в том, что первая используется для повторной генерации исключений, а вторая - для создания вновь созданного исключения. Если вы используете второй для повторной генерации исключения, он будет рассматривать его как новое исключение и заменять всю информацию стека из того места, где оно было первоначально создано.

Таким образом, вы не должны использовать ни одну из альтернатив в вопросе. Вы не должны использовать catch без параметров, и вы должны использовать его throw;для повторной генерации исключения.

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

try {
   ...
} catch (IOException e) {
   ...
   throw;
}

Если вы хотите добавить какую-либо информацию при повторной генерации исключения, вы создаете новое исключение с исходным исключением в качестве внутреннего исключения, чтобы сохранить всю информацию:

try {
   ...
} catch (IOException e) {
   ...
   throw new ApplicationException("Some informative error message", e);
}
Гуффа
источник