As long as there has been software, there have been bugs. Exceptions are a hierarchy of classes designed to help developers capture, inspect, and handle errors. For C# developers and many other languages, the way we work with exceptions is in the try/catch construct.

In this post, we’ll look at ExceptionDispatchInfo, a unique supplement to a .NET developer’s exception handling toolbox.

The Good Old Try/Catch Block

As .NET developers, we’re all familiar with the try/catch format of handling exceptions.

try {
    A();
}
catch (Exception e)
{
    throw;
}

When our application throws an exception from the calling of method A, we know that the catch block will capture that exception. It’s a fundamental understanding of all .NET developers.

There are a few caveats with this approach.

  1. We can choose to handle the exception, thus not letting the exception bubble any further.
  2. We can choose to handle specific exceptions, like NotImplementedException or ArgumentException.
  3. We can choose to remove all stack information by replacing throw with throw e. In a sense, reestablishing the location of the exception.

These all have valid use cases, but what if I said that we could buffer our exception in a container designed to contain an exception?

The ExceptionDispatchInfo Class

The .NET Framework introduced the ExceptionDispatchInfo class around version 4.5.

The ExceptionDispatchInfo object stores the stack trace information and Watson information that the exception contains at the point where it is captured. The exception can be thrown at another time and possibly on another thread by calling the ExceptionDispatchInfo.Throw method. The exception is thrown as if it had flowed from the point where it was captured to the point where the Throw method is called. Microsoft

Let’s take a look at an example.

ExceptionDispatchInfo Example

Given we have the following code, we expect our code to throw an exception of NotImplementedException at runtime. Additionally, we expect to see the methods of A, B, and C in our exception stack information.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;

namespace Preview
{
    class Program
    {
        public static void Main(string[] args)
        {
            try {
                A();
            }
            catch (Exception e)
            {
                throw;
            }
        }

        public static void A()
        {
            B();
        }

        private static void B()
        {
            C();
        }

        private static void C()
        {
            throw new NotImplementedException();
        }
    }
}

No surprise, we do.

Unhandled exception. System.NotImplementedException: The method or operation is not implemented.
   at Preview.Program.C() in /Users/khalidabuhakmeh/Projects/Dotnet/Preview/Program.cs:line 34
   at Preview.Program.B() in /Users/khalidabuhakmeh/Projects/Dotnet/Preview/Program.cs:line 29
   at Preview.Program.A() in /Users/khalidabuhakmeh/Projects/Dotnet/Preview/Program.cs:line 24
   at Preview.Program.Main(String[] args) in /Users/khalidabuhakmeh/Projects/Dotnet/Preview/Program.cs:line 14

How do we use ExceptionDispatchInfo in this scenario?

class Program
{
    public static void Main(string[] args)
    {
        ExceptionDispatchInfo edi = null;
        try {
            A();
        }
        catch (Exception e)
        {
            // captured on line 15
            edi = ExceptionDispatchInfo.Capture(e);
        }
        
        // throw again on line 24
        edi.Throw();
    }

    public static void A()
    {
        B();
    }

    private static void B()
    {
        C();
    }

    private static void C()
    {
        throw new NotImplementedException();
    }
}

What’s surprising is that we see the original stack trace and the line where we throw the exception.

Unhandled exception. System.NotImplementedException: The method or operation is not implemented.
   at Preview.Program.C() in /Users/khalidabuhakmeh/Projects/Dotnet/Preview/Program.cs:line 39
   at Preview.Program.B() in /Users/khalidabuhakmeh/Projects/Dotnet/Preview/Program.cs:line 34
   at Preview.Program.A() in /Users/khalidabuhakmeh/Projects/Dotnet/Preview/Program.cs:line 29
   at Preview.Program.Main(String[] args) in /Users/khalidabuhakmeh/Projects/Dotnet/Preview/Program.cs:line 15
--- End of stack trace from previous location ---
   at Preview.Program.Main(String[] args) in /Users/khalidabuhakmeh/Projects/Dotnet/Preview/Program.cs:line 24

Conclusion

Awesome! ExceptionDispatchInfo allows us to store an exception for a period within our AppDomain (the class is not serializable). This storage mechanism can help us to perform clean-up tasks or other necessary steps before throwing an exception. As long as it’s within the same app domain, this class can help us transport and throw an exception. The transport approach can be useful in programming models that have background threads and a main thread.

If you’ve used ExceptionDispatchInfo in your codebase, I’d love to hear how and where you found it useful.