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

4 comentarios en “La (casi) muerte al #if/#endif a manos del atributo Condicional”

  1. Pues no sé, yo veo el mismo coste de mantenimiento y exactamente los mismos problemas… simplemente has cambiado una cosa por otra, sin más.

    Sigues teniendo elementos que estarán presentes en unas compilaciones y no en otras, o incluso diferentes elementos, con lo que no ganas absolutamente nada.

    Además, desde mi punto de vista empeoras la legibilidad: es bastante más difícil darse cuenta de que ese código irá en debug o en release… aunque el texto quede más bonito…

    A veces, un puñetazo en lo ojos como es la compilación condicional mediante #if es necesaria para eso: que te golpee los ojos y veas que eso es condicional…

  2. El coste de mantenimiento sí se ve afectado.

    Da igual de que hablemos, cada vez que repetimos las mismas instrucciones N veces (los #if intercalados en el código) afecta directamente al coste de mantenimiento.

    Imagina que deseas cambiar los criterios de las variables de entorno a evaluar, no es lo mismo tocarlo en un sitio que en N, con la propensión a errores que eso implica.

  3. Vale me parece que es una buena alternativa pero es una solucion dependiente del compilador y del lenguaje de programacion, no es una solucion basado en un nuevo enfoque replicable en todos los lenguages de programacion.

    Personalmente no me gustan las directivas #if pero las veces que he intentado resolver este problema con otro metodo solo lo he complicado mas.

    buen aporte

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *