C# 6 exception filters and how they are more than syntactic sugar
volatileread.com
volatileread.com
The background is that there is a piece of functionality in Windows previously called Watson, now called Windows Error Reporting. It's that dialog that pops up when an application crashes that asks if you'd like to report the crash to Microsoft. If you do, it sends a crash dump to Microsoft, where it can then be routed to the owner of the application, even if the application isn't actually developed by Microsoft.
So a lot of developers in the know have designed their applications so that unrecoverable errors actually purposely cause the application to crash so that they can get a dump from WER. One of the standard ways of doing this is simply wrapping your code in a catch at the top level and then calling Environment.FailFast to crash the process.
The problem is that sometimes the stack is destroyed in this process. Note that I didn't say the stack trace -- the stack is destroyed. You can see this if you catch and rethrow an exception. The locals from the original exception are gone, so now the dump is far more difficult to debug.
Exception filters let you get around this because they don't actually pop anything, including the exception, from the stack, so if you FailFast, you crash with the full stack of the process.
A number of .NET developers had previously figured this out and were doing a bunch of nasty stuff to get this behavior, like including a VB net module with an exception filter fail-fast, or ildasm/ilasming their assembly. We tried a bunch of these methods on the compiler and they were all a source of many really obnoxious bugs, so eventually another compiler developer and I just said "screw it" and implemented exception filters in C#. All the bugs went away and we lived happily ever after.
Oh, and now exception filters are in the language.
With that purpose in mind, wrapping your application on the top level with try ... catch (Exception) {abort()} negates any benefit that you may get out of exception filters.
bool _operation_X_success = false;
try {
...
_operation_X_success = true;
} finally [
if (!_operation_X_success) {
Log("blah blah");
}
}You also need a boolean and a few extra lines of control code as your example shows.
Rethrowing an exception should not screw up the stack-trace. But they do, so we need to use the irritating exception filter mechanic.
That more or less gives you what Microsoft calls 'edit and continue' in every program, where your high-level code decides what code does the edit and decides on how to continue.
Some discussion with real-world usage scenarios at http://lambda-the-ultimate.org/node/1544. Example at http://c2.com/cgi/wiki?LispRestartExample
The other alternative is what C# 6 added: eliminating the need to rethrow in as many circumstances in the first place, so 'logically' unhandled exceptions can be actually unhandled.
If there's some unique data which must be carried back up the stack with the Exception it's a different story.
See "Example 3" here:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms6...
in particular, how FilterFunction is used.
In fact, changing state in an exception filter method that always returns false sounds terrible to me. What if you actually need to do something with that exception, or to clean up state? What if the Log method does something wrong that you need to debug? Exception filters are handy, but I hope to never see them used like that.
Now the new null propagation operator, on the other hand, get me some of that, and I hope to see it used everywhere.
And that's not what this article is about.
Doesn't look great. It's not easy to understand what this code does. I'd better rewrite it this way:
catch if(IsExceptionLogged())
So we can see here "Is" keyword, it gives us a hint that this function returns boolean. We can see the main operation, and it's more human-readable. And we can easily understand what do we mean here. Because in case of Log() we have to think - should we catch if it was logged or if it was not logged.
Maybe it is useful in some cases. But unfortunately the code quality doesn't depend on the tools one uses, and it's nothing about language features. 99% of developers don't know how to use C# 1.0. There are so still a lot for them to learn.
Maybe even
catch if(IsExceptionHandled())
is better. Because I don't see any reasons why should we catch exceptions if it was logged. Yep, it was logged - is it a good decision factor to catch? Logging often can be disabled in configuration.
"IsExceptionLogged" implies that the function is being called for its return value, to work out whether the exception is being looked or not, but in this usecase the Log function is being called for its side affects, and the return value is a boolean simply to appease the type checker.