I’m finally sorting through the stuff I came home from the PDC with, and I found a note that I jotted down pertaining to the When clause in VB.NET. While I was manning the booth, a reader named Matt Ryan started chatting with me about the blog. He mentioned that he’d read about the When clause in exception handling and noticed that every time it was discussed, the person talking about it would say “well, it’s cool, but I don’t know what you’d use it for.” He then proceeded to tell me about an interesting use that his team had found for the When clause.
First, to step back, the When clause is an optional Boolean filter expression in a Try/Catch statement that determines whether or not a Catch clause handles an exception. In the following stupid example, the Catch block will only be executed if the value of SomeValue is 10:
Sub CauseException(ByVal SomeValue As Integer)
Try
Throw New ApplicationException()
Catch ex As Exception When SomeValue = 10
' Do something
End Try
End Sub
The use that Matt’s team had found for the When clause was to use it as an efficient way to log exceptions. Without the When clause, the only way to log an exception without handling it is to catch the exception, log it and then rethrow it, as in:
Sub DoWork()
Try
' Do some work
Catch ex As Exception
LogException(ex)
Throw
End Try
End Sub
(Hopefully people are familiar with rethrowing exceptions. A ‘Throw’ statement by itself in a Catch block will rethrow the exception without disturbing all the useful stack trace information stored in the exception. Leave a comment if anyone would like elaboration.)
The problem with this is that you’re incurring extra overhead in logging exceptions this way. The CLR exception model is two-pass. Put simply (which is about the level that I understand it), when an exception is thrown the CLR makes a first pass up the call stack looking for an exception handler to handle the exception. When it finds a suitable handler, it then goes back and makes a second pass up the stack, unwinding the stack up to the point where the exception handler is. When you log exceptions in the above way, you incur the two-pass overhead twice: once to get to the exception handler that does the logging and then again when the logging exception handler rethrows the exception.
A more elegant way of logging, then, would be to use the When clause:
Sub DoWork()
Try
' Do some work
Catch ex As Exception When LogException(ex)
' Do no work
End Try
End Sub
In this example, LogException is now a function that always returns False. The beauty of the When clause is that the CLR executes it as a part of its first pass up the stack looking for exception handlers. So in this case, the When clause will be executed, the exception will be logged, LogException will return False and the CLR will continue up the stack looking for a suitable exception handler. No extra overhead of throwing exceptions twice. Pretty neat!
Ironically, I’m just starting to do some work on the Whidbey VB.NET compiler to improve its integration with the Windows error logging service (you know, the thing that pops up the dialog “An error has occured in foo.exe. Would you like to report the error to Microsoft?”), and it turns out we’re going to end up doing exactly the same thing in C++. Basically, we want to catch an exception that is “in flight,” pass it off to the Windows error logging service and then allow the exception to continue on to be handled elsewhere. The way we do this is, you guessed it, we create an exception handler whose filter expression calls the Windows error logging service and then returns False.
Synchronicity at work.