Jakie najlepsze praktyki należy wziąć pod uwagę przy wychwytywaniu wyjątków i ponownym ich rzucaniu? Chcę się upewnić, że śledzenie Exception
obiektu InnerException
i stosu są zachowane. Czy istnieje różnica między następującymi blokami kodu w sposobie, w jaki sobie z tym radzą?
try
{
//some code
}
catch (Exception ex)
{
throw ex;
}
Vs:
try
{
//some code
}
catch
{
throw;
}
źródło
ExceptionDispatchInfo.Capture(ex).Throw(); throw;
w .NET +4,5 stackoverflow.com/questions/57383/...Jeśli wyrzucisz nowy wyjątek z początkowym wyjątkiem, zachowasz również początkowy ślad stosu.
źródło
AggregateException
powinien być stosowany tylko w wyjątkach od zagregowanych operacji. Na przykład jest on wyrzucany przez klasyParallelEnumerable
iTask
CLR. Użycie powinno prawdopodobnie pójść za tym przykładem.W rzeczywistości istnieją pewne sytuacje, w których
throw
statystyka nie zachowa informacji StackTrace. Na przykład w poniższym kodzie:StackTrace wskaże, że wiersz 54 zgłosił wyjątek, chociaż został zgłoszony w wierszu 47.
W sytuacjach takich jak ta opisana powyżej istnieją dwie opcje zachowania oryginalnego StackTrace:
Wywołanie wyjątku.InternalPreserveStackTrace
Ponieważ jest to metoda prywatna, należy ją wywołać za pomocą refleksji:
Mam tę wadę, że polegam na prywatnej metodzie w celu zachowania informacji StackTrace. Można go zmienić w przyszłych wersjach .NET Framework. Powyższy przykład kodu i zaproponowane poniżej rozwiązanie zostało wyodrębnione z bloga internetowego Fabrice MARGUERIE .
Wywołanie wyjątku.SetObjectData
Poniższa technika została zasugerowana przez Anton Tykhyy jako odpowiedź na In C #, w jaki sposób mogę ponownie wprowadzić wyjątek wewnętrzny bez utraty pytania śledzenia stosu .
Chociaż ma tę zaletę, że polega wyłącznie na metodach publicznych, zależy to również od następującego konstruktora wyjątków (którego niektóre wyjątki opracowane przez strony trzecie nie implementują):
W mojej sytuacji musiałem wybrać pierwsze podejście, ponieważ wyjątki podniesione przez bibliotekę innej firmy, z której korzystałem, nie implementowały tego konstruktora.
źródło
Kiedy to robisz
throw ex
, w zasadzie rzucasz nowy wyjątek i tracisz informacje o oryginalnym stosie śledzenia.throw
jest preferowaną metodą.źródło
Zasadą jest unikanie łapania i rzucania podstawowego
Exception
obiektu. To zmusza cię do nieco mądrzejszego podejścia do wyjątków; innymi słowy powinieneś mieć wyraźny haczykSqlException
, aby twój kod obsługi nie zrobił czegoś złego zNullReferenceException
.Jednak w prawdziwym świecie łapanie i rejestrowanie wyjątku podstawowego jest również dobrą praktyką, ale nie zapomnij przejść całej rzeczy, aby uzyskać jakąkolwiek
InnerExceptions
.źródło
Zawsze powinieneś używać „rzut”; aby ponownie wprowadzić wyjątki w .NET,
Zobacz to, http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx
Zasadniczo MSIL (CIL) ma dwie instrukcje - „rzut” i „rzut”:
Zasadniczo widzę powód, dla którego „throw ex” przesłania ślad stosu.
źródło
throw ex;
, że powróci - w Javie tak! Ale musisz podać tę informację tutaj, aby uzyskać odpowiedź klasy A. (Chociaż wciąż nadrabiam zaległościExceptionDispatchInfo.Capture
odpowiedzi od jeuoekdcwzfwccu .)Nikt nie wyjaśnił różnicy między
ExceptionDispatchInfo.Capture( ex ).Throw()
zwykłymthrow
, więc oto jest. Jednak niektórzy zauważyli problemthrow
.Całkowitym sposobem na ponowne wyłapanie przechwyconego wyjątku jest użycie
ExceptionDispatchInfo.Capture( ex ).Throw()
(dostępne tylko w .Net 4.5).Poniżej znajdują się przypadki niezbędne do przetestowania tego:
1.
2)
3)
4
Przypadek 1 i przypadek 2 dają ślad stosu, w którym numerem kodu źródłowego dla
CallingMethod
metody jest numerthrow new Exception( "TEST" )
wiersza.Jednak przypadek 3 da ci ślad stosu, w którym numerem linii kodu źródłowego dla
CallingMethod
metody jest numer liniithrow
wywołania. Oznacza to, że jeślithrow new Exception( "TEST" )
linia jest otoczona innymi operacjami, nie masz pojęcia, pod którym numerem linii został zgłoszony wyjątek.Przypadek 4 jest podobny do przypadku 2, ponieważ zachowany jest numer wiersza oryginalnego wyjątku, ale nie jest to rethrow, ponieważ zmienia typ pierwotnego wyjątku.
źródło
throw ex;
a to najlepsza odpowiedź ze wszystkich.Kilka osób faktycznie pominęło bardzo ważny punkt - „rzut” i „rzut ex” mogą zrobić to samo, ale nie dają istotnej informacji, która jest linią, w której wystąpił wyjątek.
Rozważ następujący kod:
Kiedy wykonujesz „rzut” lub „rzut ex”, otrzymujesz ślad stosu, ale linia # będzie miała numer 22, więc nie możesz dowiedzieć się, która linia dokładnie zgłasza wyjątek (chyba że masz tylko 1 lub kilka wiersze kodu w bloku try). Aby uzyskać oczekiwaną linię nr 17 w twoim wyjątku, musisz zgłosić nowy wyjątek z oryginalnym śledzeniem stosu wyjątków.
źródło
Możesz także użyć:
A wszelkie zgłoszone wyjątki zwiększą się do następnego poziomu, który je obsługuje.
źródło
Zdecydowanie użyłbym:
To pozwoli zachować twój stos.
źródło
throw
; na przykład możesz wyczyścić jednorazowe (gdzie TYLKO wywołujesz je z błędem), a następnie wyrzucić wyjątek.Do Twojej wiadomości Właśnie przetestowałem to i ślad stosu zgłoszony przez „throw”; nie jest całkowicie poprawnym śladem stosu. Przykład:
Ślad stosu poprawnie wskazuje na początek wyjątku (zgłaszany numer linii), ale numer linii zgłaszany dla foo () jest linią rzutu; instrukcja, stąd nie można powiedzieć, które z wywołań bar () spowodowało wyjątek.
źródło