Exceptions: What happens when an exception occurs inside a catch or inside a when (C# 6) & how smart is the compiler with whens which have a constant expressions or whens with duplicate expressions?

C# 6

Want to learn about other C# 6 features? Check out the full list of articles & source code on GitHub

Exceptions, the bit of code that makes everything break! Here are some interesting thoughts around how exceptions work, which were brought up during a recent presentation I gave on C# 6.

Note: Based on VS 2015 CTP 6, this may change by RTM.

Exceptions in a catch block

Starting off, let us look at what happens if you raise an exception in the catch block of a try…catch. This isn’t anything new to C# 6, but it is worth a recap.

public static void OldPain()
{
    try
    {
        throw new MyException { Id = 200 };
    }
    catch (MyException ex)
    {
        throw new Exception("WHAT");
    }
}

In this scenario, the first exception (MyException) is caught, and then the second exception is raised. Since there is no try…catch for the second exception, it runs normally and bubbles up until it is either caught higher up or the app crashes. In this example, that means an exception of type Exception will be raised.

What if we add a catch all?

As a slight variation, what happens if you add another catch below the first one? Does the second catch catch the exception raised in the first catch block? For example:

public static void OldPain()
{
    try
    {
        throw new MyException { Id = 200 };
    }
    catch (MyException ex)
    {
        throw new Exception("WHAT");
    }
    catch (Exception ex)
    {
        // stuff
    }
}

Nope, not at all. It exits the entire try…catch scope, so that second catch does nothing for what we are testing. In the example above, that means an exception of type Exception will be raised—same as with the first scenario.

Exceptions in when

C# 6 adds the when keyword, allowing us to add a filter to our catch blocks. This feature is called Exception Filters, and you may find articles using if as the keyword, but that has changed to when. Prior to C# 6, the condition to run a catch block was just the exception type. Now, with C# 6, it is the exception type—and, optionally, the result of the when expression. So what happens if the when expression raises an exception? For example:

public static bool Test()
{
    throw new Exception("WHAT!");
}

public static void OldPain()
{
    try
    {
        throw new MyException { Id = 200 };
    }
    catch (MyException ex) when (Test())
    {
    }
}

I assumed it would work in a similar way to the exceptions inside the catch block we looked at above. I was wrong. Any exception in the when condition is swallowed up! In this example, the MyException will be raised since there is no catch block that can handle it—the when has failed due to the exception being raised in Test().

What if we add a catch all?

As with the first example, does adding a second catch block here change the behavior?

public static bool Test()
{
    throw new ApplicationException("WHAT!");
}

public static void OldPain()
{
    try
    {
        throw new MyException { Id = 200 };
    }
    catch (MyException ex) when (Test())
    {
    }
    catch (ApplicationException ex)
    {
        // will this be run?
    }
}

No, this scenario runs the same as before—a MyException is raised.

Constant whens

What happens if the when expression is a constant? Is the compiler smart enough to optimize the code? To work this out, I used the AMAZING .NET Reflector, which means I can compare the original code, the IL, and the code reflected back from the IL.

Starting off with the standard filter, here’s what it looks like across all places. I suspect the reflected code shows ? since the current version of Reflector doesn’t support this.

image

Let’s change the condition to be always true. Thankfully, Visual Studio detects this and warns you:

image

But what is the result of the compiler?

image

It’s pretty much the same—with the filter still existing in the IL. Checking with false (and also release builds) shows no difference in any scenario.

Duplicate whens

The last set of scenarios are around duplicate when clauses. For example, below we have two catch blocks that are the same, and I know from running this that only the first one is executed.

public static void OldPain()
{
    try
    {
        throw new MyException { Id = 200 };
    }
    catch (MyException ex) when (ex.Id == 200)
    {
    }
    catch (MyException ex) when (ex.Id == 200)
    {
    }
}

And from checking the generated IL, both catches are included too—so no optimization around this.

As a final mad scientist idea, what if the first catch changes the condition to one the second catch can handle? Will that allow both to run? For example:

public static void OldPain()
{
    var canCatch = true;
    try
    {
        throw new MyException { Id = 200 };
    }
    catch (MyException ex) when (canCatch)
    {
        canCatch = false;
    }
    catch (MyException ex) when (!canCatch)
    {
    }
}

The answer is no. Once one catch handles it, the rest are ignored.

I hope you find this new syntax interesting! If you have any questions, please post them in the comments.