One of the fun parts of exploring and investigating the C# Language Specification is writing code that you would not write for a production application. It’s fun to write code that bends the language features.
Most developers are familiar with the concept that in .NET, exceptions are always objects that are derived from System.Exception
.
This is covered in S. 8.9.5 of the C# Language Specification (4th edition). It states:
“The expression [in a throw
statement] must denote a value of the class type System.Exception
, of a class type that derives from System.Exception
(or a subclass thereof), or of a type parameter type that has System.Exception
(or a subclass thereof) as its effective base class.”
Here are examples of throwing an object derived from System.Exception
, and a type parameter that has System.Exception
as its base class:
public static void ThrowThingsVersionOne()
{ throw new InvalidOperationException
("Because the object's state is investigating exceptions");
}
public static void ThrowThingsVersionTwo<T>() where T : System.Exception, new()
{ throw new T();
}
This section goes on to explain what happens in this instance:
public static void ThrowThingsVersionThree()
{ throw null;
}
The spec states (also in S. 8.9.5):
“If evaluation of the expression produces null
, a System.NullReferenceException
is thrown instead.”
You could write this:
public static void ThrowThingsVersionFour()
{ throw default(NullReferenceException);
}
Or, if you wanted to confuse the developers that read your code later, you could write this:
public static void ThrowThingsVersionFive()
{ throw default(InvalidOperationException);
}
Now, we are starting to get to some harder to read code. I’ve added an explanatory comment. Without it, we’re beginning to write code that can confuse other developers. Let’s see how far we can take this.
Let’s try this:
public static void ThrowThingsVersionSix()
{ throw default(string);
}
The compiler prevents this sort of evil. I’ve tried to throw null
, but I’ve declared it such that the compile time type is System.String
. That’s not derived from System.Exception
, so the compiler flags the error.
Well, let’s learn how good the compiler is at determining what’s being thrown. First, let’s try an implicitly typed local variable:
public static void ThrowThingsVersionSeven()
{ var e = new InvalidOperationException
("Because the object's state is investigating exceptions"); throw e;
}
That compiles, and throws the expected InvalidOperationException
. Implicitly typed variables have a compile time type that matches the right hand side of the assignment. How about this:
public static void ThrowThingsVersionEight()
{ object e = new InvalidOperationException
("Because the object's state is investigating exceptions"); throw e;
}
It doesn’t compile, because the compile time type of ‘e
’ is System.Object
. Well, let’s try to coerce the compiler and bend it to our evil will:
public static void ThrowThingsVersionNine()
{ dynamic e = new InvalidOperationException
("Because the object's state is investigating exceptions"); throw e;
}
The compiler still thwarts our evil intent. This doesn’t compile, because ‘dynamic
’ doesn’t derive from System.Exception
. Because the language rules for dynamic allow us to try to convert it to any type, we can bend the compiler to our evil will:
public static void ThrowThingsVersionTen()
{ dynamic e = new InvalidOperationException
("Because the object's state is investigating exceptions"); throw (System.Exception)e;
}
Bwa ha ha ha, I say. We’ve finally found a path to force the compiler to pure evil.
To finish, let’s try to throw something that’s not an exception. Without running the code, try and figure out what this might do:
public static void ThrowThingsVersionEleven()
{ dynamic e = "Because the object's state is investigating exceptions"; throw (System.Exception)e;
}
I’ll update this post toward the end of the week with the explanation.