VB Catch … When: Why so special?


The VB Catch syntax has a particular feature not present in C#: When.  It allows users to filter expressions based on something other than their type.  Any arbitrary code can enter a When block to decide whether or not to handle an Exception

    Sub Sub1()
Try
DoSomeAction()
Catch ex As Exception When Filter(ex)
Stop
End Try
End Sub

Newsgroups often ask, “Why’s this so special? I could effectively get the same behavior out of C# by doing the following.”

static void Sub1()
{
try
{
DoSomeAction();
}
catch (Exception ex)
{
if (Filter(ex))
{
throw;
}
HandleException();
}
}

This is true to an extent.  In both cases the code is handling an exception and making a decision, via calling Filter, as to whether or not to handle the exception.  The subtle difference is when the Filter method is called. 


In VB the When statement is actually implemented as an IL exception filter.  When an exception is thrown, exception filters are processed before the stack is unwound.  This means that if the Filter method created an error report that included the current stack trace, it would show the frame in which the exception occurred. 


For example, in the code above if DoSomeAction() threw and the stack was examined in the Filter expression, the following stack would show up.


image


Notice how the DoSomeAction method is clearly visible?  This is incredibly powerful for features like error reporting and investigation.  It also allows you to set powerful breakpoints where the exact state of the error can be examined and not just the post mortem. 


Alternatively, code executed in the C# block will occur after the stack is unwound.  This gets rid of the culprit.  As long as your not in optimized code you can usually use the stack trace properties to get the source of the exception but you won’t be able to examine the live state of the error.


image

Comments (5)

  1. Tom says:

    Jared,

    Great information!  I never have seen any reference to this before.

    Also, could someone give uus a tip on what to do when the My Namespace UnhandledException handler fails.  In the event viewer I am getting this error:

    "Application ErrorApplication has generated an exception that could not be handled"

    What can we do to debug this?

    Thanks

    Tom

  2. Ted says:

    You should not rely on the VB when filtering to capture the call stack on an exception.  This happens because developers do not handle errors/exceptions where they occur and just wrap large blocks of nested functions in a try catch block.  That is and has been a known lazy and bad practice since C++ in the early 1990s.  It would not pass any of our code reviews given that it allows for multiple block of sql connect, query, query, disconnects to be wrapped in a single try catch block.

  3. @Ted

    For the first sentence why not?  It it’s perfectly legal to capture the callstack without handling the exception.  Let the process crash but you can write out a log file (or prompt during development).  For post mortem debugging this is extremely valuable information.  

    As for the rest, I don’t advocate actually handling general purpose exceptions.  The code in the article is just for demonstration purpose.  However people can handle exceptions poorly with or without the Filter feature.

  4. Ted says:

    Write code that is production quality and handle errors where the occur.  

    Truley fatal errors like .NET runs out of memory are a special case and still should be handled at the main() program level via a dump or log message if possible.