Una de las partes más divertidas de explorar e investigar la especificación del lenguaje C# es la de escribir código que no sabes que nunca escribirías para una aplicación en producción. Es divertido escribir código que multiplique las características del lenguaje.
La mayoría de los desarrolladores están familiarizados con el concepto de que en .NET, las excepciones son siempre objetos que derivan de System.Exception.
Esto se trata en la S. 8.9.5 de la especificación del lenguaje C# (4ª edición). Afirma:
"La expresión [en una sentencia throw] debe indicar un valor de la clase tipo System.Exception, de un tipo de clase que se deriva de System.Exception (o una subclase de la misma), o de un tipo de parámetro de tipo que tiene System.Exception (o una subclase de la misma) como su clase base efectiva. "
Todo esto son ejemplos sobre como lanzar un objeto derivado de System.Exception, y un parámetro de tipo que tenga a System.Exception como su clase base:
public static void ThrowThingsVersionOne() { throw new InvalidOperationException ("Because the object's state is investigating exceptions"); } public static void ThrowThingsVersionTwo() where T : System.Exception, new() { throw new T(); }
En esta sección vamos a explicar lo que sucede en este caso:
public static void ThrowThingsVersionThree() { throw null; }
También en S. 8.9.5
“Si la evaluación de la expresión es nula, un System.NullReferenceException se lanza en su lugar."
Puedes escribir esto:
public static void ThrowThingsVersionFour() { throw default(NullReferenceException); }
O, si quieres confundir a los desarrolladores que lean tu código después, puedes escribir esto:
public static void ThrowThingsVersionFive() { // Throws a NullReferenceException: throw default(InvalidOperationException); }
Ahora, empezando a llegar a uno de las partes más difíciles de entender. De todas maneras hay un comentario explicativo en inglés. Sin él, estaremos escribiendo código que podría confundir a otros desarrolladores. Vamos a ver hasta qué lejos podemos llegar con esto.
Vamos a intentar esto:
public static void ThrowThingsVersionSix() { throw default(string); }
El compilador evita este tipo de maldades. He intentado lanzar un null, pero lo había declarado de tal manera que el tipo de tiempo de compilación es System.string. Esto no se deriva de System.Exception, por lo que el compilador lanza un error.
Bueno, vamos a aprender lo bueno que es el compilador a la hora de determinar lo que está siendo lanzado. En primer lugar, vamos a probar con una variable local:
public static void ThrowThingsVersionSeven() { var e = new InvalidOperationException ("Because the object's state is investigating exceptions"); throw e; }
Esto compila, y lanza la esperada InvalidOperationException. Implícitamente las variables tienen un tipo de tiempo de compilación que coincide con la asignación correcta. Qué tal esto:
public static void ThrowThingsVersionEight() { object e = new InvalidOperationException ("Because the object's state is investigating exceptions"); throw e; }
Esto no compila, porque el tipo de tiempo de compilación 'e' es System.Object. Bueno, vamos a tratar de coaccionar el compilador y multiplicarlo a nuestra voluntad:
public static void ThrowThingsVersionNine() { dynamic e = new InvalidOperationException ("Because the object's state is investigating exceptions"); throw e; }
El compilador aún se frustra con nuestras malas artes. Esto no compila, porque "dynamic" no deriva de System.Exception. Debido a que las reglas del lenguaje de dinámica nos permiten tratar de convertirlo a cualquier tipo, podemos multiplicar el compilador a nuestra mala voluntad:
public static void ThrowThingsVersionTen() { dynamic e = new InvalidOperationException ("Because the object's state is investigating exceptions"); throw (System.Exception)e; }
Finalmente hemos encontrado una manera para forzar al compilador a que se trague nuestras maldades. Para terminar, vamos a tratar de lanzar algo que no sea una excepción. Sin ejecutar el código, averigua qué podría hacer esto:
public static void ThrowThingsVersionEleven() { dynamic e = "Because the object's state is investigating exceptions"; throw (System.Exception)e; }