A Subtle DIfference in Catch Blocks

What's the difference in the following two catch blocks?

public static int DifferencesInCatch(int x)
{
    int value = x + 20;
    
    try
    {
        value = value / x;
    }
    catch(Exception) {}
    
    try
    {
        value = value / x;
    }
    catch {}
    
    return value;
}

At first glance...not much. They both seem like they're going to catch the general exception type. And you'd be right...but there is a difference.

Take a look at the IL:

.method public hidebysig static int32 DifferencesInCatch(int32 x) cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 'value')
    L_0000: ldarg.0 
    L_0001: ldc.i4.s 20
    L_0003: add 
    L_0004: stloc.0 
    L_0005: ldloc.0 
    L_0006: ldarg.0 
    L_0007: div 
    L_0008: stloc.0 
    L_0009: leave.s L_000e
    L_000b: pop 
    L_000c: leave.s L_000e
    L_000e: ldloc.0 
    L_000f: ldarg.0 
    L_0010: div 
    L_0011: stloc.0 
    L_0012: leave.s L_0017
    L_0014: pop 
    L_0015: leave.s L_0017
    L_0017: ldloc.0 
    L_0018: ret 
    .try L_0005 to L_000b catch [mscorlib]System.Exception handler L_000b to L_000e
    .try L_000e to L_0014 catch object handler L_0014 to L_0017
}

Look at the 2nd catch block's type. It's object, not Exception.

Now, in the CLR it's valid to throw anything; it doesn't have to be a subclass of Exception. This is used by some languages (e.g. C++), but most .NET developers don't even know about this. In fact, in 2.0 if code throws something that's not Exception-based, it'll wrap that object with a RuntimeWrappedException (I've discussed this feature in detail here).

Anyway, here's the design issue I'm struggling with. In my ExceptionFinder code, I'm trying to iron bugs out. A couple of weeks ago, I thought I saw some bizarre types listed in the results - they were more than just the "object" type. Unfortunately, I lost all my notes on what I had seen (damnit!), so I thought I could add code in that would not allow non-Exception types to be reported. In fact, I considered this to be a bug in the program and I changed the implementation to throw a custom exception when a type is reported that's not an Exception type. But...it's also a completely valid case! For example, if code calls "throw" in a catch block like the 2nd one in the original code sample, what's on the stack is an object of...System.Object. Most likely it's some kind of Exception-based object, but it's possible that it could be something else.

I'm thinking of breaking my LeakedExceptionsCollection into two separate nodes: one that contains all the Exception-based exceptions, and those that don't. Frankly, most of the analysis will find Exception-based exceptions, but at least this way I'll know if I find an exception of type System.String I can trace down what method it occurred in and on what instruction.

* Posted at 08.22.2008 03:42:43 PM CST | Link *

Blog History