Ответ
Ключевое отличие — в сохранении оригинальной трассировки стека (stack trace), что критично для отладки.
throw;— повторно выбрасывает перехваченное исключение, сохраняя полный стек вызовов.throw ex;— выбрасывает то же исключение заново, но сбрасывает точку возникновения на текущую строку, обрезая стек вызовов.
Пример, демонстрирующий проблему:
void InnerMethod() {
throw new InvalidOperationException("Ошибка на низком уровне");
}
void OuterMethod() {
try {
InnerMethod();
}
catch (Exception ex) {
// ПЛОХО: стек будет указывать на эту строку, а не на InnerMethod
// throw ex;
// ХОРОШО: стек сохранит путь: OuterMethod -> InnerMethod
throw;
}
}
Когда что использовать:
- Всегда используйте
throw;при повторной генерации того же исключения в блокеcatch. Это стандартная практика. throw ex;практически никогда не нужен. Его единственное гипотетическое применение — намеренное сокрытие внутренней структуры кода (security through obscurity), что является антипаттерном.- Если нужно создать новое исключение с дополнительным контекстом, оберните оригинальное:
catch (Exception ex) { throw new ApplicationException("Не удалось обработать запрос", ex); // ex становится InnerException }
Ответ 18+ 🔞
А, слушай, тут есть одна тонкость, про которую многие нихуя не знают, а потом орут, что отладчик пиздит. Всё дело в том, как ты кидаешь исключение обратно.
Смотри, есть два способа: throw; и throw ex;. И разница — это пиздец какая важная, потому что от неё зависит, увидишь ты, где ошибка вылезла на самом деле, или будешь ебаться как слепой котёнок.
throw;— это как сказать: «Слушай, вот эта ошибка, она не моя, я её просто поймал и передаю дальше, как есть». И самое главное — весь стек вызовов, вся цепочка, кто кого вызывал, остаётся нетронутой. Отладчик тебе красиво покажет: вот тут накосячили, вот откуда пошло, вот как докатились до жизни такой.throw ex;— а вот это уже подстава. Ты как бы говоришь: «О, ошибка! Ну-ка, я её перевыброшу, но от моего имени!». И в этот момент весь предыдущий стек вызовов нахуй стирается. Отладчик покажет, что исключение родилось прямо в этой строчке сthrow ex;. А где оно реально возникло? Хуй его знает. Ище потом, блядь.
Вот, смотри на примере, как это выглядит:
void InnerMethod() {
// Допустим, тут реальная проблема
throw new InvalidOperationException("Ошибка на низком уровне");
}
void OuterMethod() {
try {
InnerMethod();
}
catch (Exception ex) {
// ПЛОХО, НЕ ДЕЛАЙ ТАК: стек будет указывать сюда, на строчку ниже. Будто ошибка здесь.
// Ты реальный источник потеряешь. Зачем тебе эта головная боль?
// throw ex;
// ХОРОШО, ДЕЛАЙ ТАК: стек сохранит всю правду — OuterMethod -> InnerMethod.
// Поймёшь, где корень зла.
throw;
}
}
Так когда что юзать, чтобы не быть мудаком?
- В 99.9% случаев, когда ловишь и снова кидаешь то же самое исключение — юзай
throw;. Это святое правило, запомни его, как «отвернись — плюнь». Сохраняешь всю историю, все логи, отладчик тебе друг. throw ex;— это говно, не нужен он почти никогда. Разве что ты параноик и хочешь специально спрятать внутренности своей программы (security through obscurity), но это, прости, тупой антипаттерн. Не выёбывайся.- Если поймал ошибку и хочешь добавить от себя пояснение — не перекидывай старое, оберни его в новое! Сделай его
InnerException. Тогда и контекст твой добавится, и оригинальная трассировка не потеряется.
catch (Exception ex) {
// Вот так — культурно, информативно, и стек не похерится.
throw new ApplicationException("Не удалось обработать запрос, чувак", ex); // ex уходит во InnerException
}
Короче, запомни: throw ex — это как подменить труп в морге. Следы все затереть. Не делай так, если не хочешь потом самому себе глаза выколоть при отладке.