La (casi) muerte al #if/#endif a manos del atributo Condicional

Como los más viejos del lugar ya conocen, los bloques #if/#endif han sido empleados para generar diferentes builds a partir del mismo código fuente. Personalmente nunca he estado muy orgulloso con este mecanismo, complicaba la legibilidad del código y desperdigaba las llamadas. Dichas (malas) características aumentan el coste de mantenimiento y crean un mecanismo demasiado fácil del que abusar.

Aunque lo tenía en la lista de enemigos hasta hace bien poco no le había dedicado el tiempo necesario para buscarle alternativa, pero eso ya acabó…

El escenario típico antes de emplear dicho atributo era:

1- Crearse el método entre ambas directivas #if/#endif
2- Incluir también cada una de las llamadas al mismo entre una pareja de directivas #if/#endif (teniendo que crear más código fuente que la propia llamada…)

El sustituto es el atributo Conditional.

Veamos un código de ejemplo:

[ Conditional( "DEBUG" ) ] 
private void Comprobar( ) 
{ 
  ... 
} 

Elegante, verdad? Al decorar el método con el atributo condicional, es el compilador, el que en base a las variables de entorno, decide si se realizarán las llamadas o no, obteniendo

Con "DEBUG"

public string Nombre 
{ 
  get 
  { 
    Comprobar( ); 
    return _nombre; 
  } 
  set 
  { 
    Comprobar( ); 
    _nombre = value; 
    Comprobar( ); 
  } 
} 

Sin "DEBUG"

public string Nombre 
{ 
  get 
  { 
    return _nombre; 
  } 
  set 
  { 
    _nombre = value; 
  } 
} 

El contenido del método Comprobar no varía, solo las llamadas al mismo son modificadas. El único coste que pagamos es el espacio de disco consumido con la función "Comprobar". Debemos tener en cuenta que la función no será cargada en memoria y compilada JIT a menos de que sea llamada.

También podemos crear métodos que dependan de más de una variable de entorno:

[ Conditional( "DEBUG" ), Conditional( "TRACE" ) ] 
private void Comprobar( ) 
{
   …
}

Observar que ambos atributos son combinados por "OR", si queremos combinarlos por “AND” debemos utilizar #if/#endif:

#if ( DEBUG && TRACE ) 
#define AMBOS 
#endif 

De ahí la (casi) muerte al #if/#endif