DEV Community

timyhac
timyhac

Posted on

Reviewing re-throwing

TL;DR

  • Implicit rethrow allows you to do something with the Exception, and then let it bubble up as if your code didn't touch it.
  • Explicit rethrow mutates the Exception object before passing it, by resetting the stack trace
  • In both cases, the Exception object is passed by reference.

I've been dabbling in c# for a while, but I figured that my knowledge of python and javascript should have given me a bit of an edge. I'm certainly not starting from scratch, but I also wouldn't consider myself an expert in either of those languages.

So, to prove to myself that I understand and can use c#, I've set myself a target to pass the 70-483 exam by the end of 2019.

I decided to take a look around at previous exams, and stumbled upon this site. The first question I was given was around re-throwing an exception including it's original stack trace. The requirements were:

  • Log all exceptions by using the LogException() method of the ExceptionLoggerclass.
  • Rethrow the original exception, including the entire exception stack.

It was a multiple choice question, and these were the options:

// Option A
catch (Exception ex)
{
    ExceptionLogger.LogException(ex);
    throw;
}

// Option B
catch (Exception ex)
{
    ExceptionLogger.LogException(ex);
    throw ex;
}

// Option C
catch
{
    ExceptionLogger.LogException(new Exception());
    throw;
}

// Option D
catch
{
    ExceptionLogger.LogException(ex);
    throw;
}

Straight away, I knew that Option C couldn't be it, because the ExceptionLogger was being passed a fresh new Exception (clearly without any of the data of the original exception), and for that matter, didn't catch have to have a parentheses to describe the Exception type? It probably wouldn't even compile! (Later I discovered that it does...)

It also couldn't be Option D because it clearly couldn't compile (ex isn't defined anywhere), although to be honest I assumed it wouldn't compile because of the 'missing' parentheses on the catch statement.

So it was down to options A and B.

I chose option B.

The answer was option A.


In retrospect, I wasn't really sure what throw without an exception did. I had seen it before, but I guess I thought that it wouldn't be any different to throw ex.

Option B is called an Explicit rethrow, whereas Option A is called an Implicit rethrow. In most situations you want to use an implicit rethrow because it preserves the stack, and it turns out that this is a well known rule.

But my goal is to deeply understand, and not just to blindly copy rules, so I went about writing some code to test what was going on...


Iteration 1

To verify that the answer was actually correct, I decided to create some code that would let me see what happens when throwing an exception explicitly:

try
{
    try
    {
        throw new DivideByZeroException();
    }
    catch (Exception ex1)
    {
        Console.WriteLine(ex1); // System.DivideByZeroException ... Line 13
        throw ex1;  // or throw(ex1);
    }
}
catch (Exception ex2)
{
    Console.WriteLine(ex2); // System.DivideByZeroException ... Line 18
}

The console prints that there is a DivdeByZeroException on lines 13 and 18.

Even though I had read through the aforementioned articles, it was still surprising! After all, the exception happens only on line 13.

So I removed the explicit rethrow from the catch block to see whether what they said was actually true:

try
{
    try
    {
        throw new DivideByZeroException();
    }
    catch (Exception ex1)
    {
        Console.WriteLine(ex1); // System.DivideByZeroException ... Line 13
        throw;
    }
}
catch (Exception ex2)
{
    Console.WriteLine(ex2); // System.DivideByZeroException ... Line 13
}

Both WriteLine() calls showed that the exception was on line 13.

Iteration 2

There must be a new Exception being created, I reasoned, that is why the second WriteLine() call shows the exception on line 18!

So I modified my code to the following:

Exception A = null;
Exception B = null;
try
{
    try
    {
        throw new DivideByZeroException();
    }
    catch (Exception ex1)
    {
        A = ex1;
        throw ex1;
    }
}
catch (Exception ex2)
{
    B = ex2;
}
Console.WriteLine(A); // System.DivideByZeroException ... Line 15
Console.WriteLine(B); // System.DivideByZeroException ... Line 15

Now both WriteLine() calls show the exception to be falling on line 20, not on line 15 (now the location of the DivideByZeroException).

I was shocked!

After grappling with this for a while, I realised that when we throw an exception, the exception object is passed to the catch statement by reference, not by value. Therefore, A and B were pointing to the same object.
I could quickly check this theory by looking at whether the objects were equal:

Console.WriteLine(A == B);    // Output: True

In hindsight, this seems obvious - but it was critical to my understanding of what was going on here.

So the only difference between my previous iteration and this one was the point at which the exception was serialized to text; before the throw, and after the throw.

Exception A was being passed to the second catch block by being the parameter to the throw statement, and this was what was mutating it:

Key Takeaway: An explicit throw actually changes the Exception object by resetting it's stack trace.


This blog post by Jalpesh Vadjama also helped in my understanding of the inner workings.

Top comments (0)