jaka jest różnica pomiędzy
try { ... }
catch{ throw }
i
try{ ... }
catch(Exception e) {throw new Exception(e.message) }
Niezależnie od tego, że druga pokazuje wiadomość?
jaka jest różnica pomiędzy
try { ... }
catch{ throw }
i
try{ ... }
catch(Exception e) {throw new Exception(e.message) }
Niezależnie od tego, że druga pokazuje wiadomość?
Odpowiedzi:
throw; ponownie zgłasza oryginalny wyjątek i zachowuje jego oryginalny ślad stosu.
throw ex;zgłasza oryginalny wyjątek, ale resetuje ślad stosu, niszcząc wszystkie informacje o śladzie stosu aż do catchbloku.
throw ex;throw new Exception(ex.Message);jest jeszcze gorszy. Tworzy zupełnie nową Exceptioninstancję, tracąc oryginalny ślad stosu wyjątku, a także jego typ. (np IOException.).
Ponadto niektóre wyjątki zawierają dodatkowe informacje (np ArgumentException.ParamName.).
throw new Exception(ex.Message); zniszczy te informacje.
W niektórych przypadkach możesz chcieć zawinąć wszystkie wyjątki w niestandardowy obiekt wyjątku, aby można było podać dodatkowe informacje o tym, co robił kod, gdy wyjątek został zgłoszony.
Aby to zrobić, zdefiniuj nową klasę, która dziedziczy Exception, dodaj wszystkie cztery konstruktory wyjątków i opcjonalnie dodatkowy konstruktor, który pobiera, InnerExceptiona także dodatkowe informacje, i wyrzuć nową klasę wyjątku, przekazując ją exjako InnerExceptionparametr . Przekazując oryginał InnerException, zachowujesz wszystkie właściwości oryginalnego wyjątku, w tym ślad stosu.
throw new MyCustomException(myMessage, ex);oczywiście.
ex.Message, co jest gorsze.
[Serializable()].
throw;rzeczywisty numer wiersza, w którym wystąpił wyjątek, zostanie zastąpiony numerem wiersza throw;. Jak sobie z tym radzisz? stackoverflow.com/questions/2493779/…
Pierwsza zachowuje oryginalny ślad stosu:
try { ... }
catch
{
// Do something.
throw;
}
Drugi umożliwia zmianę rodzaju wyjątku i / lub wiadomości i innych danych:
try { ... } catch (Exception e)
{
throw new BarException("Something broke!");
}
Istnieje również trzeci sposób, w którym można przekazać wyjątek wewnętrzny:
try { ... }
catch (FooException e) {
throw new BarException("foo", e);
}
Polecam użycie:
Jeszcze jedna kwestia, której nikt nie zauważył:
Jeśli nic nie zrobisz w swoim bloku catch {}, próba ... złapania jest bezcelowa. Widzę to cały czas:
try
{
//Code here
}
catch
{
throw;
}
Albo gorzej:
try
{
//Code here
}
catch(Exception ex)
{
throw ex;
}
Najgorsze jeszcze:
try
{
//Code here
}
catch(Exception ex)
{
throw new System.Exception(ex.Message);
}
throwponownie zgłasza przechwycony wyjątek, zachowując ślad stosu, jednocześnie throw new Exceptiontracąc niektóre szczegóły przechwyconego wyjątku.
Zwykle używałbyś throwsamego siebie do rejestrowania wyjątku bez pełnej obsługi go w tym momencie.
BlackWasp ma dobry artykuł zatytułowany Throwing Exceptions in C # .
Zgłoszenie nowego wyjątku powoduje zniszczenie aktualnego śladu stosu.
throw;zachowa oryginalny ślad stosu i prawie zawsze jest bardziej przydatna. Wyjątkiem od tej reguły jest sytuacja, gdy chcesz zawinąć wyjątek w niestandardowy wyjątek własny. Powinieneś wtedy:
catch(Exception e)
{
throw new CustomException(customMessage, e);
}
throwsłuży do ponownego zgłaszania przechwyconego wyjątku. Może to być przydatne, jeśli chcesz zrobić coś z wyjątkiem przed przekazaniem go do łańcucha wywołań.
Użycie throwbez żadnych argumentów zachowuje stos wywołań do celów debugowania.
Twój drugi przykład zresetuje ślad stosu wyjątku. Pierwsza z nich najdokładniej zachowuje pochodzenie wyjątku. Rozpakowałeś również oryginalny typ, który jest kluczem do ustalenia, co faktycznie poszło nie tak ... Jeśli drugi jest wymagany do funkcjonalności - np. Aby dodać rozszerzone informacje lub ponownie zawinąć specjalnym typem, takim jak niestandardowy 'HandleableException', po prostu upewnij się, że właściwość InnerException jest również ustawiona!
Najważniejsza różnica polega na tym, że drugie wyrażenie usuwa typ wyjątku. Typ wyjątku odgrywa istotną rolę w wyłapywaniu wyjątków:
public void MyMethod ()
{
// both can throw IOException
try { foo(); } catch { throw; }
try { bar(); } catch(E) {throw new Exception(E.message); }
}
(...)
try {
MyMethod ();
} catch (IOException ex) {
Console.WriteLine ("Error with I/O"); // [1]
} catch (Exception ex) {
Console.WriteLine ("Other error"); // [2]
}
Jeśli foo()rzuca IOException, [1]blok catch złapie wyjątek. Ale kiedy zostanie bar()rzucona IOException, zostanie zamieniona na zwykłą Exceptionmrówkę, która nie zostanie złapana przez [1]blok catch.
throw lub throw ex, oba służą do zgłaszania lub ponownego zgłaszania wyjątku, kiedy po prostu rejestrujesz informacje o błędzie i nie chcesz wysyłać żadnych informacji z powrotem do dzwoniącego, po prostu rejestrujesz błąd w catch i opuść. Ale jeśli chcesz wysłać znaczące informacje o wyjątku do dzwoniącego, którego używasz throw lub throw ex. Teraz różnica między throw i throw ex polega na tym, że throw zachowuje ślad stosu i inne informacje, ale throw ex tworzy nowy obiekt wyjątku, a zatem oryginalny ślad stosu zostaje utracony. Kiedy więc powinniśmy użyć throw i throw e, jest jeszcze kilka sytuacji, w których możesz chcieć ponownie zgłosić wyjątek, np. Zresetować informacje o stosie wywołań. Na przykład, jeśli metoda znajduje się w bibliotece i chcesz ukryć szczegóły biblioteki przed kodem wywołującym, niekoniecznie chcesz, aby stos wywołań zawierał informacje o metodach prywatnych w bibliotece. W takim przypadku możesz przechwycić wyjątki w metodach publicznych biblioteki, a następnie wyrzucić je ponownie, tak aby stos wywołań zaczynał się od tych metod publicznych.
Żadna z odpowiedzi tutaj nie pokazuje różnicy, co może być pomocne dla osób, które mają trudności ze zrozumieniem różnicy. Rozważ ten przykładowy kod:
using System;
using System.Collections.Generic;
namespace ExceptionDemo
{
class Program
{
static void Main(string[] args)
{
void fail()
{
(null as string).Trim();
}
void bareThrow()
{
try
{
fail();
}
catch (Exception e)
{
throw;
}
}
void rethrow()
{
try
{
fail();
}
catch (Exception e)
{
throw e;
}
}
void innerThrow()
{
try
{
fail();
}
catch (Exception e)
{
throw new Exception("outer", e);
}
}
var cases = new Dictionary<string, Action>()
{
{ "Bare Throw:", bareThrow },
{ "Rethrow", rethrow },
{ "Inner Throw", innerThrow }
};
foreach (var c in cases)
{
Console.WriteLine(c.Key);
Console.WriteLine(new string('-', 40));
try
{
c.Value();
} catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
}
Który generuje następujący wynik:
Bare Throw:
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
at ExceptionDemo.Program.<>c.<Main>g__bareThrow|0_1() in C:\...\ExceptionDemo\Program.cs:line 19
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Rethrow
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<>c.<Main>g__rethrow|0_2() in C:\...\ExceptionDemo\Program.cs:line 35
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Inner Throw
----------------------------------------
System.Exception: outer ---> System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 43
--- End of inner exception stack trace ---
at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 47
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Sama rzut, jak wskazano w poprzednich odpowiedziach, wyraźnie pokazuje zarówno oryginalną linię kodu, która się nie powiodła (linia 12), jak i dwa inne punkty aktywne na stosie wywołań, gdy wystąpił wyjątek (linie 19 i 64).
Dane wyjściowe przypadku ponownego rzucenia pokazują, dlaczego jest to problem. Gdy wyjątek zostanie ponownie wygenerowany w ten sposób, wyjątek nie będzie zawierał oryginalnych informacji o stosie. Zauważ, że uwzględnione są tylko throw e(linia 35) i najbardziej zewnętrzny punkt stosu wywołań (linia 64). Trudno byłoby wyśledzić metodę fail () jako źródło problemu, jeśli w ten sposób rzucisz wyjątki.
Ostatni przypadek (innerThrow) jest najbardziej rozbudowany i zawiera więcej informacji niż którykolwiek z powyższych. Ponieważ tworzymy nowy wyjątek, mamy szansę dodać informacje kontekstowe (wiadomość „zewnętrzną”, ale możemy również dodać do słownika .Data nowy wyjątek), jak również zachować wszystkie informacje z oryginału wyjątek (w tym łącza pomocy, słownik danych itp.).