W wersji .NET 4.5 pojawiła się klasa ExceptionDispatchInfo, która nie jest zbyt znana. W większości przypadków nie jest na szczęście potrzebna, ale np. w wielowątkowości może ułatwić bardzo specyficzne zadania wyrzucania i filtrowania wyjątków.
Klasyczny przykład użycia to zebranie w kolekcji różnych wyjątków, a następnie wyrzucenie ich ponownie (prawdopodobnie po nałożeniu odpowiedniego filtra) w jakimś innym miejscu. Taki scenariusz to jest dokładnie sytuacja w której klasa została użyta w TPL i w konstrukcji await\async.
Załóżmy, że mamy kilka metod:
private static void Throw() { Throw2(); } private static void Throw2() { Throw3(); } private static void Throw3() { throw new NotImplementedException(); }
Chcemy wywoływać Throw, która następnie wywoła kilka metod pod rząd, wywołując na końcu wyjątek. Sytuacja wielowątkowa może wyglądać następująco:
var exceptions = new ConcurrentBag<ExceptionDispatchInfo>(); ThreadPool.QueueUserWorkItem(_ => { try { Throw(); } catch (Exception ex) { ExceptionDispatchInfo exceptionDispatchInfo = ExceptionDispatchInfo.Capture(ex); exceptions.Add(exceptionDispatchInfo); } });
Jak widzimy, w kodzie tylko zbieramy wyjątki i nie wyrzucamy ich od razu. Taka sytuacja ma często miejsce w TPL, na przykład w przypadku równoległych pętli.
ExceptionDispatchInfo ma metodę Capture, która owinie odpowiednio wyjątek. Dzięki temu nie stracimy informacji o StackTrace.
Na końcu, w kodzie zbiorczym, chcemy wyjątek wyrzucić ponownie, ale koniecznie z zachowaniem oryginalnego stackstrace. W praktyce prawdopodobnie dokonujemy w tym miejscu również jakiś filtrów. Najprostszy przykład może wyglądać następująco:
foreach (ExceptionDispatchInfo exceptionDispatchInfo in exceptions) { try { exceptionDispatchInfo.Throw(); } catch (Exception ex) { Console.WriteLine("{0}", ex); } }
ExceptionDispatchInfo zatem zawiera dwie najważniejsze metody:
- Capture – przechowanie wyjątku
- Throw – wyrzucenie go podobnie
Wygenerowany wyjątek będzie zawierał stary (oryginalny) stacktrace:
Innymi słowy, jeśli chcemy wyrzucić wyjątek ponownie w innym miejscu, bez modyfikacji StackTrace (np. poprzez WrappedException), możemy skorzystać z przedstawionej klasy.