Tabla de Contenido
0. Introducción1. 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]:
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.
La salida de la compilación y compilación:
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:
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:
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:
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.