Wzywam, poprzez refleksję, metodę, która może powodować wyjątek. Jak mogę przekazać wyjątek mojemu rozmówcy bez otaczania go odbiciem opakowania?
Powtarzam wyjątek wewnętrzny, ale to niszczy ślad stosu.
Przykładowy kod:
public void test1()
{
// Throw an exception for testing purposes
throw new ArgumentException("test1");
}
void test2()
{
try
{
MethodInfo mi = typeof(Program).GetMethod("test1");
mi.Invoke(this, null);
}
catch (TargetInvocationException tiex)
{
// Throw the new exception
throw tiex.InnerException;
}
}
Odpowiedzi:
W .NET 4.5 jest teraz
ExceptionDispatchInfo
klasa.Pozwala to uchwycić wyjątek i ponownie go wyrzucić bez zmiany śledzenia stosu:
Działa to na każdym wyjątku, nie tylko
AggregateException
.Został on wprowadzony dzięki
await
funkcji języka C #, która rozpakowuje wewnętrzne wyjątki odAggregateException
instancji, aby asynchroniczne funkcje języka bardziej przypominały funkcje języka synchronicznego.źródło
throw;
po wierszu .Throw (), ponieważ kompilator nie będzie wiedział, że .Throw () zawsze zgłasza wyjątek.throw;
nigdy nie zostanie wywołany, ale przynajmniej kompilator nie będzie narzekał, jeśli twoja metoda wymaga obiektu zwrotnego lub jest funkcją asynchroniczną.throw;
. Jeśli użyjeszthrow ex.InnerException;
śledzenia stosu, zostanie on ponownie zainicjowany w punkcie, w którym zostanie ponownie wyrzucony.ExceptionDispatchInfo.Capture(ex.InnerException ?? ex).Throw();
Możliwe jest zachowanie śladu stosu przed ponownym rzuceniem bez odbicia:
To marnuje wiele cykli w porównaniu do dzwonienia
InternalPreserveStackTrace
przez buforowanego delegata, ale ma tę zaletę, że polega tylko na funkcjonalności publicznej. Oto kilka typowych wzorców użycia funkcji zachowywania śledzenia stosu:źródło
InternalPreserveStackTrace
(około 6% wolniej przy 10000 iteracjach). Bezpośredni dostęp do pól za pomocą refleksji jest około 2,5% szybszy niż wywoływanieInternalPreserveStackTrace
e.Data
słownika z ciągiem znaków lub unikalnym kluczem obiektowym (static readonly object myExceptionDataKey = new object ()
ale nie rób tego, jeśli musisz serializować wyjątki w dowolnym miejscu). Unikaj modyfikacjie.Message
, ponieważ możesz mieć kod gdzieś, który analizujee.Message
. Parsowaniee.Message
jest złe, ale może nie być innego wyboru, np. Jeśli musisz użyć biblioteki innej firmy ze złymi praktykami wyjątkowymi.Myślę, że najlepszym rozwiązaniem byłoby po prostu umieszczenie tego w bloku catch:
A później wyodrębnij innerexception.
źródło
Wywołaj metodę rozszerzenia wyjątku, zanim ją wyrzucisz, zachowa oryginalny ślad stosu.
źródło
Action<Exception>
? Tutaj zastosuj metodę statycznąNikt nie wyjaśnił różnicy między
ExceptionDispatchInfo.Capture( ex ).Throw()
zwykłymthrow
, więc oto jest.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 oryginalnego wyjątku.
źródło
Jeszcze więcej refleksji ...
Należy pamiętać, że może to nastąpić w dowolnym momencie, ponieważ pola prywatne nie są częścią API. Zobacz dalszą dyskusję na temat Bugzilli Mono .
źródło
InternalPreserveStackTrace
metody wewnętrznej byłoby lepsze, ponieważ robi to samo i jest mniej prawdopodobne, że zmieni się w przyszłości ...Po pierwsze: nie trać wyjątku TargetInvocationException - jest to cenna informacja, kiedy będziesz chciał debugować różne rzeczy.
Po drugie: Zawiń TIE jako InnerException we własnym typie wyjątku i umieść właściwość OriginalException, która łączy się z tym, czego potrzebujesz (i zachowaj cały stos połączeń nienaruszony).
Po trzecie: pozwól TIE wygasnąć z twojej metody.
źródło
Chłopaki, jesteście fajni .. Niedługo będę nekromantą.
źródło
.Invoke()
.Kolejny przykładowy kod, który wykorzystuje serializację / deserializację wyjątków. Nie wymaga faktycznego typu wyjątku, aby można go serializować. Używa także tylko metod publicznych / chronionych.
źródło
Na podstawie odpowiedzi Paula Turnersa opracowałem metodę rozszerzenia
return ex
ist nigdy nie osiągnął, ale zaletą jest to, że można używaćthrow ex.Capture()
jako jedną wkładką więc kompilator nie zgłosinot all code paths return a value
błąd.źródło