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 bubbled 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 and, 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 which allows us to add a filter to our catch blocks. This feature is called Exception Filters & 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 which can handle it as 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 behaviour?
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 optimise the code? To work this out I used the AMAZING .NET Reflector which means I can compare the original code, the IL which was generated and the code which was reflected back from the IL.
Starting off with the standard filter, here is what it looks like across places. I suspect the reflected code is showing ? since the version of reflector out currently doesn’t support this.
Let us change the condition to be always true. Thankfully Visual Studio will detect this and warn you:
But what is the result of compiler?
It is pretty much the exact same there, with the filter existing in the IL still :/ Checking with false (and also release builds) shows no difference in any scenario :/
Duplicate whens
The last set of scenarios are around for duplicate when clauses, for example below we have two catch blocks which 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 in there too. So no optimisation 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 nope. Once one catch handles it the rest are ignored.
I hope you find this new syntax interesting and if you have any questions, please post them in the comments!