Різниця між спробувати / зловити / кинути та спробувати / зловити (е) / кинути е


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;
}

Перший і другий блоки спробу-лову - ТОЧНО те саме, вони просто перезавантажують поточний виняток, і це виключення збереже своє "джерело" та слід стека.

Третій блок-спроб - інший. Коли він викидає виняток, він змінить джерело та слід стека, так що, здається, виняток було викинуто з цього методу, з цього самого рядка 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) { ... }

Удачі!


34

Перший зберігає слід стека, а другий скидає його. Це означає, що якщо ви використовуєте другий підхід, слід стека винятку завжди починатиметься з цього методу, і ви втратите початковий слід виключення, який може бути згубним для того, хто читає журнали виключень, оскільки він ніколи не дізнається первинну причину винятку .

Другий підхід може бути корисним, коли ви хочете додати додаткову інформацію до сліду стека, але він використовується таким чином:

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

Є запис у блозі, де обговорюються відмінності.


Ну це чудові речі, щоб знати!
Майлз

так навіщо використовувати другий тоді? краще використовувати лише перший?
Карим

1
Другий стане в нагоді, коли вам потрібно перевірити конкретні, крімinoinons - на думку спадає OutOfRangeException - або потрібно записати повідомлення тощо. Перший, здається, обробник виняткових кодів, схожий на спробу {} catch (...) {} в c ++.
3Dave

1
Девід, це стосується лише частини вилову (виняток e) . І це окремо від throwпроти throw e.
Хенк Холтерман

6

Ви повинні використовувати

try { }
catch(Exception e)
{ throw }

якщо ви хочете зробити щось за винятком перед повторним закиданням (наприклад, ведення журналу). Самотній кидок зберігає стеговий слід.


і що буде, якщо я замінив тут "кидок" на "кидок е"?
Карим

5

Різниця між параметричним уловом і 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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.