miércoles, 9 de octubre de 2013

Receta C# No. 1-6: Inclusión de Código Modo Selectivo en Tiempo de Compilación

Tabla de Contenido

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
4. Práctica: Código C#
5. Uso de la Solución
6. Conclusiones
7. Glosario
8. Referencias

0. Introducción

En situaciones en las que la aplicación esté orientada para plataformas y ambientes de ejecución diferentes, es necesario (en muchas ocasiones) que se tenga que escribir código de acuerdo a los recursos disponibles o soportados (APIs dependientes del sistema operativo, por ejemplo) por la plataforma. C# provee una serie de directivas de pre-procesador para soportar esta necesidad en el diseño de una aplicación.

1. Problema

Se requiere activar o desactivar secciones de código dependiendo de la plataforma en la que el código fuente sea compilado.

2. Solución

El lenguaje de programación C# provee directivas de preprocesador [1] que permite incluir o excluir secciones de código durante el tiempo de compilación. Entre estas se pueden nombrar:
  • #if [2]
  • #elif [3]
  • #else [4]
  • #endif [5]
Además, el atributo System.Diagnostics.ConditionalAttribute permite marcar aquellos métodos que serán llamados de acuerdo al valor obtenido de la evaluación por cualquier de las directivas nombradas arriba.

Para controlar la inclusión o exclusión de código condicional (o modo selectivo) se usan las directivas #define [7] y #undef [8]. E inclusive podemos hacer lo mismo desde el compilador de C# de la línea de comandos por medio de la opción (o switch) /define.

3. Discusión de la Solución

Los chequeos de lógica que permiten determinar la plataforma adyacente en la que corre la aplicación conducen a crear código de forma excesiva, y aún más, cuando las llamadas a métodos corresponden a distintas versiones ajustadas a los requerimientos de la plataforma en cuestión.

Una primera solución a este problema de código para diferentes plataformas y las consecuencias de bajo rendimiento por múltiples chequeos puede consistir en crear diferentes versiones de compilación para cada plataforma, sin embargo la mantenibilidad [10] se verá ofuscada debido al ardúo trabajo que requiere mantener código fuente y sus respectivos procesos de compilación y despliegue.

C# permite a través de las directivas [1] identificar aquellos bloques de código que nos interesan para ser incluídos en el assembly generado en tiempo de compilación. Entre las directivas tenemos #if, #elif, #else, y #endif y algunos operadores lógicos que se muestran en la Tabla 1 [11]:
Operadores para directivas de preprocesador.
Tabla 1. Operadores lógicos soportados por la directiva #if..#endif en C# [11].
El funcionamiento de las directivas de preprocesador condicionales opera de forma similar a las sentencias de selección (ver artículo Sentencias en C# - Parte 2).

En [11] nos advierten:
Caution You must be careful not to overuse conditional compilation directives and not to make your conditional expressions to complex; otherwise, your code can quickly become confusing and unmanageable -especially as your projects become larger.
La definición de un símbolo (en este caso el identificador de la plataforma que queremos soportar: #define win7) se realiza a través de la directiva #define. El alcance de la definición es para todo el archivo en el que haya sido declarada; además, deben ser declaradas al inicio del archivo, inclusive, antes de las sentencias de importación using. Así mismo, C# provee la directiva #undef. Ésta permitirá al programador asegurar la no inclusión de símbolos (e.g., sistema operativo no soportado: #undef win2000).

[Nota: En próximos artículos (seguramente muy lejanos) discutiré sobre el uso de una alternativa más elegante (como dicen en [11]) y es del atributo System.Diagnostics.ConditionalAttribute [6], el cual es aplicado a métodos.]

4. Práctica: Código C#

En este código fuente C# de ejemplo vamos a definir varios símbolos (permitidos y no permitidos). Desde la línea de comandos pasaremos al compilador por medio de la opción /define otros símbolos que en código fuente serán evaluados y determinar la plataforma en que será compilado el assembly. E inclusive utilizaré el atributo Conditional para el método de prueba MetodoCondicional.

El uso del atributo Conditional en la línea anterior a la declaración de MetodoCondicional[Conditional("DEBUG")]. Esta marca indica que el método sólo se ejecutará cuando el símbolo DEBUG haya sido declarado.

En el método Main (líneas 19-40) se hace uso de las directivas #if, #elif, y #endif para determinar la plataforma para la que está siendo compilado el assembly.

5. Uso de la Solución

Para compilar el código fuente del archivo Plataforma.cs utilizamos el siguiente comando:


La salida de la compilación y compilación:
Sin utilizar DEBUG
Figura 1. Compilación sin el símbolo DEBUG.

Ahora vamos a utilizar el símbilo DEBUG desde la línea de comandos a la hora de compilar. Entonces utilizamos el siguiente comando:



Miremos el resultado de la ejecución:
Compilación con DEBUG
Figura 2. Compilación con el símbolo DEBUG.

6. Conclusiones

El modelo de compilación basado en directivas de preprocesador extra que provee C#, nos permite crear distintas versiones basadas en la plataforma destino en que deseemos desplegar la aplicación de nuestro proyecto de ingeniería de software. Hemos aprendido como definir símbolos a través de directivas de preprocesador, a definir sus valores, y marcar métodos para llamadas condicionales.

7. Glosario

  • API
  • Directiva
  • Plataforma
  • Preprocesador
  • Sistema Operativo

8. Enlaces & Literatura

[1]: C# Preprocessor Directives - http://msdn.microsoft.com/en-us/library/ed8yd1ha.aspx
[2]: #if (C# Reference) http://msdn.microsoft.com/en-us/library/4y6tbswk.aspx
[3]: #elif (C# Reference) - http://msdn.microsoft.com/en-us/library/88td0y52.aspx
[4]: #else (C# Reference) - http://msdn.microsoft.com/en-us/library/87a56b46.aspx
[5]: #endif (C# Reference) - http://msdn.microsoft.com/en-us/library/hyx43has.aspx
[6]: ConditionalAttribute Class (System.Diagnostics) - http://msdn.microsoft.com/en-us/library/system.diagnostics.conditionalattribute.aspx
[7]: #define (C# Reference) - http://msdn.microsoft.com/en-us/library/yt3yck0x.aspx
[8]: #undef (C# Reference) - http://msdn.microsoft.com/en-us/library/wkxst87d.aspx
[9]: /define (C# Compiler Options) - http://msdn.microsoft.com/en-us/library/0feaad6z.aspx
[10]: Software Design for Maintainability - http://engineer.jpl.nasa.gov/practices/dfe6.pdf
[11]: Visual C# 2010 Recibes by Allen Jones and Adam Freeman. Copyright 2010 Allen Jones and Adam Freeman, 978-1-4302-2525-6.


O

No hay comentarios:

Publicar un comentario

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.