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)
Throw New ApplicationException()
Catch ex As Exception When SomeValue = 10
' Do something
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:
' Do some work
Catch ex As Exception
(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:
' Do some work
Catch ex As Exception When LogException(ex)
' Do no work
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.
I’ve often wondered – is there a reason why the finally block doesn’t have access to the exception? It seems like that would be a good alternative to the catch/log/re-throw approach – log the exception in the finally block on the way out.
I know your probably not the correct person to ask this too (I’m sure you know who is though!) but would you happen to know why C# doesn’t support a filter on a catch?
I don’t actually know why C# doesn’t have them, you should try http://msdn.microsoft.com/vcsharp/team/language/ask/default.aspx, and see what they say.
Finally blocks don’t have access to the exceptions because Finally blocks will be run regardless of whether an exception occured or not. So there may not *be* an exception for the Finally block to have access to.
I’d like to also point out another example of using the When clause. It’s not as cool as this example, but hey… the more examples, the better 😉
Well that’s certainly one good reason. 😉 Of course, there could be a way to check if there was an "active" exception, but that’s starting to get a little uglier. I do hope When makes it into C# some day – the logging scenario is tremendously useful.
Pingback: .NET From India
I believe this will extremely helpful in everyday programming. For me, at least, it would be very obvious to use the construct to ask the exception object for an error code in a manner like this:
‘ Do some communication, e.g. send a transaction
Catch ex As CommException When ex.ErrorCode = CommError.TimeOut
‘ Re-try the transaction
as we often encapsulate unmanaged communication components in .NET and translate return codes to exceptions, without introducing a new exception class for each error code.
Another helpful use:
‘ Do some work
Catch ex As MyAppException When Not ex.HasBeenLogged
‘ Log the exception to the event log or fire a WMI event
which helps prevent duplicate loggings in a multi-tiered application.
Would the LogException then Throw the exception passed in?
The efficiency bonus is nice, but what I consider to be the major benefit of doing exception logging from When is debuggability. Paul mentions the two-pass nature of the CLR’s exception system. One consequence of this is that, by the time you’ve reached the catch block, the parts of the stack between the throw and the catch have been unwound. Finally blocks have run, and the stack frames have been destroyed. (As a side note, when you "rethrow" the exception, all that is preserved is the textual stack trace in the exception object itself; as I mentioned the actual stack frames represented by that textual trace were gone before you even got to the catch.)
The debuggability implications of this are huge. Let’s say your LogException function had logic to attach a debugger if you so desired (using System.Diagnostics.Debugger.Launch). If you did so from within the catch block, you wouldn’t be able to go look at the function that originally threw the exception because its frame would already have unwound. However if you do so from within a filter, you can go look at the complete stack in a state that should be very close to what it was at the time of the exception. The same applies for any kind of crashdump taken from the filter.
Great. I like it. But in:
Sub DoWork() Try
‘ Do some work
Catch ex As Exception When LogException(ex)
‘ Do no work
End TryEnd Sub
What happens if there is a exception in the LogException function? Would it have to handle all its exceptions completely internally?
I believe if you have an exception in a When clause, the exception handling process starts all over again, so the next outer Try (if any) will handle it.
I saw a reference to a When clause on another web site suggesting you can’t throw an exception within a When clause. Is this correct?
Well, you *can* but you *shouldn’t*. The When clause is evaluated while the CLR is deciding how to handle the exception being thrown, so any further exceptions in When clauses are suppressed.