It’s stated as conventional wisdom that in .NET, a throw
expression must throw an object of a type that is System.Exception
, or derived from System.Exception
. Here’s the language from the C# specification (Section 8.9.5):
A throw
statement with an expression throws the value produced by evaluating the expression. The expression must denote a value of the class type System.Exception
, of a class type that derives from System.Exception
, or of a type parameter type that has System.Exception
(or a subclass thereof) as its effective base class.
Let’s play with the edge cases. What will this do:
throw default(NotImplementedException);
The expression is typed correctly (it is a NotImplementedException
). But, it’s null. The answer to this is in the next sentence of the C# Specification:
If evaluation of the expression produces null
, a System.NullReferenceException
is thrown instead.
That means this is also legal:
throw null;
It will throw a NullReferenceException
, as specified above.
Now, let’s see what happens if we work with a type that can be converted to an exception:
struct Exceptional
{
public static implicit operator Exception(Exceptional e)
{
return null;
}
}
An Exceptional
can be converted (implicitly) to an Exception
. But this results in a compile time error:
throw new Exceptional();
The compiler reports that the type thrown must be derived from System.Exception
. So, let’s rewrite the code so that it is:
Exception e = new Exceptional();
throw e;
The first line creates an Exceptional struct
. Assigning it to a variable of type Exception
invokes the implicit conversion. Now, it is of type Exception
, and can be thrown.
Finally, what about this expression:
dynamic d = new Exceptional();
throw d;
In most places in the language, where an expression must evaluate to a specific type, an expression of type dynamic
is allowed.
Here, we have the joy of edge cases. The spec (as I’ve quoted above) doesn’t speak to this case. Where the spec is silent, sometimes developers interpret them differently. The classic compiler (pre-Roslyn) accepts throwing an expression that is dynamic. The Roslyn (VS 2015) compiler does not. I expect this may change, and the spec may get updated to explicitly state the behavior.