Różnica między try / catch / throw a try / catch (e) / throw e

103

Jaka jest różnica pomiędzy

try { }
catch
{ throw; }

i

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

?

A kiedy powinienem użyć jednego lub drugiego?

Karim
źródło

Odpowiedzi:

151

Konstrukcje

try { ... }
catch () { ... } /* You can even omit the () here */

try { ... }
catch (Exception e) { ... }

są podobne pod tym względem, że oba będą przechwytywać każdy wyjątek rzucony wewnątrz trybloku (i jeśli nie używasz tego po prostu do rejestrowania wyjątków, należy tego unikać ). Teraz spójrz na te:

try { ... }
catch ()
{
    /* ... */
    throw;
}

try { ... }
catch (Exception e)
{
    /* ... */
    throw;
}

try { ... }
catch (Exception e)
{
    /* ... */
    throw e;
}

Pierwszy i drugi blok try-catch są DOKŁADNIE to samo, po prostu ponownie wyrzucają bieżący wyjątek, a ten wyjątek zachowa swoje „źródło” i ślad stosu.

Trzeci blok try-catch jest inny. Kiedy zgłasza wyjątek, zmieni źródło i ślad stosu, tak że będzie wyglądało na to, że wyjątek został wyrzucony z tej metody, z tej samej linii throw ew metodzie zawierającej ten blok try-catch.

Którego należy użyć? To naprawdę zależy od każdego przypadku.

Załóżmy, że masz Personklasę z .Save()metodą, która utrwali ją w bazie danych. Powiedzmy, że Twoja aplikacja Person.Save()gdzieś wykonuje tę metodę. Jeśli twoja DB odmówi uratowania Osoby, .Save()wyrzuci wyjątek. Należy użyć throwczy throw ew tym przypadku? Cóż, to zależy.

Wolę robić:

try {
    /* ... */
    person.Save();
}
catch(DBException e) {
    throw new InvalidPersonException(
       "The person has an invalid state and could not be saved!",
       e);
}

Powinno to spowodować umieszczenie wyjątku DBException jako „wewnętrznego wyjątku” zgłaszanego nowszego wyjątku. Tak więc podczas sprawdzania tego InvalidPersonException, ślad stosu będzie zawierał informacje z powrotem do metody Save (może to wystarczyć do rozwiązania problemu), ale nadal masz dostęp do oryginalnego wyjątku, jeśli go potrzebujesz.

Na koniec, gdy spodziewasz się wyjątku, powinieneś naprawdę wychwycić ten jeden konkretny wyjątek, a nie ogólny Exception, tj. Jeśli spodziewasz się wyjątku InvalidPersonException, powinieneś preferować:

try { ... }
catch (InvalidPersonException e) { ... }

do

try { ... }
catch (Exception e) { ... }

Powodzenia!

Bruno Reis
źródło
34

Pierwsza zachowuje ślad stosu, a druga resetuje go. Oznacza to, że jeśli użyjesz drugiego podejścia, ślad stosu wyjątku zawsze będzie rozpoczynał się od tej metody i utracisz oryginalny ślad wyjątku, co może być katastrofalne dla kogoś czytającego dzienniki wyjątków, ponieważ nigdy nie dowie się pierwotnej przyczyny wyjątku .

Drugie podejście może być przydatne, gdy chcesz dodać dodatkowe informacje do śladu stosu, ale jest używane w następujący sposób:

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

Jest post na blogu omawiający różnice.

Darin Dimitrov
źródło
Dobrze wiedzieć!
Myles
więc po co więc używać drugiego? czy lepiej jest używać tylko pierwszego?
Karim
1
Drugi przydaje się, gdy trzeba sprawdzić określone wyjątki - przychodzi na myśl OutOfRangeException - lub trzeba zarejestrować komunikat itp. Pierwszy wydaje się być modułem obsługi wyjątków wieloznacznych, podobnym do try {} catch (...) {} w języku c ++.
Zapisz
1
David, dotyczy to tylko części dotyczącej połowu (wyjątek e) . I które jest oddzielone od throwvs throw e.
Henk Holterman
6

Powinieneś użyć

try { }
catch(Exception e)
{ throw }

jeśli chcesz zrobić coś z wyjątkiem przed ponownym wyrzuceniem (na przykład logowanie). Samotny rzut zachowuje ślad stosu.

Otávio Décio
źródło
a co się stanie, jeśli zamienię „rzut” tutaj na „rzut e”?
Karim
5

Różnica między przechwytywaniem bez parametrów a a catch(Exception e)polega na tym, że otrzymujesz odwołanie do wyjątku. Wyjątki niezarządzane z platformy w wersji 2 są opakowane w wyjątek zarządzany, więc wyjątek bez parametrów nie jest już przydatny do niczego.

Różnica między throw;i throw e;polega na tym, że pierwszy jest używany do ponownego zgłaszania wyjątków, a drugi służy do zgłaszania nowo utworzonego wyjątku. Jeśli użyjesz drugiego do ponownego wyrzucenia wyjątku, potraktuje go jak nowy wyjątek i zastąpi wszystkie informacje o stosie z miejsca, w którym został pierwotnie wyrzucony.

Więc nie powinieneś używać żadnej z alternatyw w pytaniu. Nie należy używać przechwytywania bez parametrów, a throw;do ponownego zgłaszania wyjątku.

Ponadto w większości przypadków należy użyć bardziej szczegółowej klasy wyjątków niż klasa bazowa dla wszystkich wyjątków. Powinieneś łapać tylko te wyjątki, które przewidujesz.

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

Jeśli chcesz dodać jakiekolwiek informacje podczas ponownego zgłaszania wyjątku, utwórz nowy wyjątek z oryginalnym wyjątkiem jako wyjątek wewnętrzny, aby zachować wszystkie informacje:

try {
   ...
} catch (IOException e) {
   ...
   throw new ApplicationException("Some informative error message", e);
}
Guffa
źródło