- Published on, Time to read
- 🕒 4 min read
Is catch the same as catch(Exception)? Well, not exactly...
Is catch
the same as catch(Exception)
? Weeeell... Not exactly. There is a small subtle nuance that causes catch(Exception)
to be not hit. Chances that it happen are, however, very close to zero.
According to the CLR via C# book (Richter, 2012):
All programming languages for the CLR must support the throwing of Exception-derived objects because the Common Language Specification (CLS) mandates this. However, the CLR actually allows an instance of any type to be thrown, and some programming languages will allow code to throw non–CLS-compliant exception objects such as a String, Int32, or DateTime. The C# compiler allows code to throw only Exception-derived objects, whereas code written in some other languages allows code to throw Exception-derived objects in addition to objects that are not derived from Exception.
[...]
Although the C# compiler allows developers to throw Exception-derived objects only, prior to C# 2.0, the C# compiler did allow developers to catch non–CLS-compliant exceptions by using code like this.
private void SomeMethod() { try { // Put code requiring graceful recovery and/or cleanup operations here... } catch (Exception e) { // Before C# 2.0, this block catches CLS-compliant exceptions only // Now, this block catches CLS-compliant & non–CLS-compliant exceptions throw; // Re-throws whatever got caught } catch { // In all versions of C#, this block catches CLS-compliant // & non–CLS-compliant exceptions throw; // Re-throws whatever got caught } }
This is very interesting. I haven't seen any working example of that behavior, so I tried to reproduce that. I installed Windows XP on VirtualBox and then installed .NET Framework 1.1.x, Visual Studio 2003 (wow, such a nostalgic journey! :)).
Now, basing on the Microsoft documentation on the CA2102 rule (Microsoft, 2022), I created a simple program that throws a non-CLS-compliant exception:
The .il file:
.assembly ThrowNonClsCompliantException {}
.class public auto ansi beforefieldinit ThrowsExceptions
{
.method public hidebysig static void ThrowNonClsException() cil managed
{
.maxstack 1
IL_0000: newobj instance void [mscorlib]System.Object::.ctor()
IL_0005: throw
}
}
The .cs file:
using System;
namespace SecurityLibrary
{
class HandlesExceptions
{
void CatchAllExceptions()
{
Console.WriteLine("CLR version: " + typeof(string).Assembly.ImageRuntimeVersion);
try
{
ThrowsExceptions.ThrowNonClsException();
}
catch(Exception e)
{
Console.WriteLine("CLS compliant exception caught");
}
catch
{
Console.WriteLine("Non-CLS compliant exception caught.");
}
}
static void Main()
{
HandlesExceptions handleExceptions = new HandlesExceptions();
handleExceptions.CatchAllExceptions();
}
}
}
Then I compiled the Intermediate Language file C# file using the C# compiler. I ran the program and got the following output:
ilasm /dll ThrowNonClsCompliantException.il
csc /r:ThrowNonClsCompliantException.dll CatchNonClsCompliantException.cs
And... the catch(Exception)
is missed (expected behavior) on the .NET Framework 1.x:
CLR version: v1.1.4322
Non-CLS compliant exception caught
The beloved Windows XP screenshot:
On Windows 11 and .NET Framework (4.x) catch(Exception)
is hit (expected behavior):
CLR version: v4.0.30319
CLS compliant exception caught
The old behaviour can be reproduced by using the WrapNonExceptionThrows
flag in the RuntimeCompatibility
attribute. The value of it needs to be set to false:
using System.Runtime.CompilerServices;
[assembly:RuntimeCompatibility(WrapNonExceptionThrows = false)]
Then, the output is (expected behavior as well):
CLR version: v4.0.30319
Non-CLS compliant exception caught.
Conclusions?
catch
is the samecatch(Exception)
unless you're using C# version lower than 2.0 and non-CLS-compliant exceptions.- Or, you're using the
WrapNonExceptionThrows
flag set to false in theRuntimeCompatibility
attribute in the same type of exception. - This post does not have any practical value. It's just a curiosity that I wanted to check. I don't think anyone uses the C# version lower than 2.0 and non-CLS-compliant exceptions.
- It takes a lot of fun to go back in time and code in Visual Studio 2003 to feel the nostalgia of the Windows XP UX.