Reputation: 2622
I understand the value of ExceptionDispatchInfo.Capture(e).Throw()
(preserves the original stack trace), but what's the advantage of using Capture
early and passing the ExceptionDispatchInfo
around vs. just passing the caught Exception
around?
As a concrete example, comparing
static Exception CaptureException(Action action)
{
try
{
action();
return null;
}
catch (Exception e)
{
return e;
}
}
public void Test1()
{
ExceptionDispatchInfo.Capture(CaptureException(
() => throw new IOException("Test")))
.Throw();
}
with
static ExceptionDispatchInfo CaptureDispatchInfo(Action action)
{
try
{
action();
return null;
}
catch (Exception e)
{
return ExceptionDispatchInfo.Capture(e);
}
}
public void Test2()
{
CaptureDispatchInfo(() => throw new IOException("Test")).Throw();
}
, both give essentially the same stack traces (It's similar for an async
variation of this.). So, I don't really get why the ExceptionDispatchInfo
class exists at all, rather than just a combined ExceptionDispatchInfo.Capture(e).Throw()
method.
Upvotes: 35
Views: 12934
Reputation: 407
ExceptionDispatchInfo is used to preserve the stack trace after an Exception is thrown, allowing you to catch that exception, not throwing it immediately (as part of a catch), and to raise such exception on a later point in the future.
I found a good example of this on https://thorarin.net/blog/post/2013/02/21/Preserving-Stack-Trace.
Upvotes: 6
Reputation: 30954
You're assuming that exceptions are immutable. This is not the case - an exception's StackTrace changes when it's re-thrown.
The purpose of ExceptionDispatchInfo.Capture
is to capture a potentially mutating exception's StackTrace at a point in time:
void Foo() => throw new InvalidOperationException ("foo");
Exception original = null;
ExceptionDispatchInfo dispatchInfo = null;
try
{
try
{
Foo();
}
catch (Exception ex)
{
original = ex;
dispatchInfo = ExceptionDispatchInfo.Capture (ex);
throw ex;
}
}
catch (Exception ex2)
{
// ex2 is the same object as ex. But with a mutated StackTrace.
Console.WriteLine (ex2 == original); // True
}
// So now "original" has lost the StackTrace containing "Foo":
Console.WriteLine (original.StackTrace.Contains ("Foo")); // False
// But dispatchInfo still has it:
try
{
dispatchInfo.Throw ();
}
catch (Exception ex)
{
Console.WriteLine (ex.StackTrace.Contains ("Foo")); // True
}
Upvotes: 41