Directivas de preprocesamiento en C#
A pesar de que C# no tenga un preprocesador de código dedicado (tal como lo tiene C), aún podemos hacer uso de algunas instrucciones que modifican el comportamiento del compilador, a estas palabras se les conoce como directivas.
Sintaxis
La sintaxis de una directiva es simple: comienza por el símbolo #
seguido inmediatamente por la instrucción para el compilador. Por ejemplo:
#define esta_es_una_directiva
#region Directivas
#if esta_es_una_directiva
#endif
#endregion
Instrucciones disponibles
Podemos dividir las directivas dentro estas categorías:
Definición de símbolos de compilación
Primero que nada: En C#, un símbolo de compilación es una palabra (distinta de true
o false
) que es definida al momento de compilar el programa. Normalmente es el entorno en el que desarrollamos el encargado de definir dichos símbolos, por ejemplo, en Visual Sutdio cuando ejecutamos un programa en configuración Debug, el símbolo DEBUG
es definido para nuestro código, o en Xamarin.Forms los símbolos WINDOWS
, IOS
y ANDROID
son definidos dependiendo del proyecto que estamos compilando.
Pero nosotros también podemos definirlos mediante el uso de la directiva #define [símbolo]
y borrar la definición mediante #undef [símbolo]
. Mira el siguiente código:
#define HOLA
#define mundo
#define PIZZA
#undef mundo
En este caso para el archivo quedarán definidos los símbolos HOLA
y PIZZA
, estas definiciones se hacen con una sola intención, que veremos más adelante. Hay que señalar que las definiciones deben ser las primeras instrucciones en cada archivo.
Compilación condicional
Las directivas #if
, #else
, #elif
y #endif
permiten modificar el código que el compilador toma en cuenta para la compilación, funcionan de modo similar a un if
en C#: #if [condición]
, salvo que las condiciones simplemente son si determinado símbolo está definido o no, por ejemplo:
#if HOLA && mundo
Console.WriteLine("Hola mundo");
#elif HOLA
Console.WriteLine("Hola");
#elif mundo
Console.WriteLine("Hola");
#else
Console.WriteLine("...");
#endif
#if (HOLA || mundo) && PIZZA
Console.WriteLine("Hola o mundo y ¡pizza!");
#endif
El resultado de la ejecución es
Hola Hola o mundo y ¡pizza!
Los usos de la compilación condicional son muy variados, desde indicar que cierta porción de código únicamente se compile cuando estamos vamos a ejecutar una versión de prueba de nuestra aplicación hasta compilar distinto código fuente de acuerdo a la plataforma en la que se estará ejecutando la aplicación.
Generar errores y warnings
Podemos usar las directivas #error
y #warning
para hacer que el compilador "encuentre" errores y advertencias en nuestro código. La sintaxis es la siguiente: #error [mensaje]
y #warning [mensaje]
, por ejemplo:
#warning It is dangerous to go alone!
#error Hola! soy un error de compilación
Pero, ¿para qué querríamos provocar errores o warnings en nuestro código? yo creo que nadie. Sin embargo, usando la compilación condicional podemos "impedir" que el código se compile a menos que alguna configuración se cumpla, es por eso que normalmente estas directivas vienen rodeadas de #if
, #endif
o #elif
:
#if !DEBUG
#error Whoops, este código solamente puede ser usado en configuración debug
#endif
Supresión de warnings
Del mismo modo que podemos crear warnings, también podemos instruir al compilador a que ignore algunas de estas mediante el uso de la directiva #pragma warning disable ([identificador de advertencias])
.
Nunca ha sido bueno ignorar las advertencias en nuestro código ... sin embargo se me ocurren dos escenarios principales:
- Cuando accederemos al código de nuestro programa mediante reflexión y el compilador nos advierte que no estamos usando determinada propiedad o método.
- Cuando estamos implementando una interfaz y nuestro código únicamente llama a algunos métodos de la interfaz
Cuando deshabilitamos el reconocimiento de advertencias con la directiva anterior, el efecto permanece hasta el fin del archivo, pero podemos deshabilitar dicho efecto usando la directiva #pragma warning restore ([identificador de advertencias])
. Tomemos como ejemplo esta porción de código que como te podrás imaginar, provoca unas cuantas advertencias.
int x = 0;
return;
int zero = 0;
if (1 != null) ;
Podemos deshabilitar las advertencias una por una:
#pragma warning disable 219
int x = 0;
Varias a la vez:
#pragma warning disable 162,219
int zero = 0;
O simplemente todas a la vez:
#pragma warning disable
if (1 != null) ;
Organización de código
Estas son es seguramente las directivas con las que más frecuentemente te has encontrado, y es que su uso es mucho más común que las otras, su finalidad es agrupar físicamente bloques de código dentro de nuestro archivo fuente, además de que algunos IDE permiten colapsar el texto entre ella. Es importante decir que su inclusión no modifica el comportamiento del compilador.
Por ejemplo:
#region Super bloque de código
Console.WriteLine("Hola");
#endregion
Que en Visual Studio se ve así:
Para finalizar
Como puedes ver, las directivas nos pueden ayudar a personalizar nuestro código a partir de condiciones dictadas antes de la compilación del mismo, o a "forzar" que ciertas condiciones se cumplan para que siquiera compile. También nos ayudan a organizar nuestro código en bloques físicos dentro del archivo fuente.