Відповіді:
Конструкції
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;
}
Перший і другий блоки спробу-лову - ТОЧНО те саме, вони просто перезавантажують поточний виняток, і це виключення збереже своє "джерело" та слід стека.
Третій блок-спроб - інший. Коли він викидає виняток, він змінить джерело та слід стека, так що, здається, виняток було викинуто з цього методу, з цього самого рядка throw e
в методі, що містить цей блок-пробування.
Який із них слід використовувати? Це дійсно залежить від кожного конкретного випадку.
Скажімо, у вас 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);
}
Є запис у блозі, де обговорюються відмінності.
throw
проти throw e
.
Ви повинні використовувати
try { }
catch(Exception e)
{ throw }
якщо ви хочете зробити щось за винятком перед повторним закиданням (наприклад, ведення журналу). Самотній кидок зберігає стеговий слід.
Різниця між параметричним уловом і a catch(Exception e)
полягає тому, що ви отримуєте посилання на виняток. З версії Framework 2 некеровані винятки загортаються в керований виняток, тому виняток без параметрів більше не корисний ні для чого.
Різниця між throw;
іthrow e;
полягає в тому, що перший використовується для повторного скидання винятків, а другий використовується для викидання новоствореного винятку. Якщо ви використовуєте другий для повторного скидання винятку, він трактуватиме його як новий виняток і замінить всю інформацію про стек, звідки його було викинуто.
Отже, ви не використовували жодної з альтернатив у питанні. Ви не повинні використовувати параметр улову, а ви повинні throw;
повторно скинути виняток.
Також у більшості випадків для всіх винятків слід використовувати більш конкретний клас винятку, ніж базовий клас. Вам слід спіймати лише ті винятки, які ви передбачаєте.
try {
...
} catch (IOException e) {
...
throw;
}
Якщо ви хочете додати будь-яку інформацію під час повторного скидання винятку, ви створюєте новий виняток із вихідним винятком як внутрішній виняток, щоб зберегти всю інформацію:
try {
...
} catch (IOException e) {
...
throw new ApplicationException("Some informative error message", e);
}