lunes, 3 de marzo de 2014

Comprobación de Tipos en Tiempo de Compilación y Ejecución en C#

Tabla de Contenido

0. Introducción
1. Ciclo de Vida del Proceso de Compilación
1.1 Análisis léxico
1.2 Análisis semántico
1.3 Análisis sintáctico
1.4 Generación de código intermedio
2. Sistema de Tipos
3. Modos de Comprobación de Tipos
3.1 Estático
3.1.1 Tipeo fuerte
3.1.2 Tipeo débil
3.2 Dinámico
3.2.1 Dinamismo en la plataforma .NET
3.2.2 Dynamic Language Runtime (DLR)
4. Conclusiones
5. Glosario
6. Enlaces & Literatura

0. Introducción

En este espacio nos vamos a adentrar en el entendimiento de la comprobación de tipos en tiempo de compilación y ejecución. Hemos aprendido que tanto el compilador y la CLR de C# proveen mecanismos que automatizan muchas tareas que de lo contrario el programador tendría que hacer a pulso: liberación y localización de memoria,  manejo de excepciones, comprobación de tipos, &c. La infraestructura de .NET y en consecuencia del lenguaje de programación nos ayuda a desentendernos de estos asuntos; y el sistema de tipos nos ayudará a detectar problemas en la manipulación de tipos. Veamos cómo. Bienvenidos.

1. Ciclo de Vida del Proceso de Compilación

El proceso (ciclo de vida) de compilación [4] está compuesto por, principalmente, las siguientes fases:
  • Análisis léxico
  • Análisis semántico
  • Análisis sintáctico
  • Generación de código intermedio
En la Figura 1 [4] se describe visualmente este proceso.
Ciclo de vida compilación.
Figura 1. Ciclo de vida compilación [4].

1.1 Análisis léxico

Durante el análisis léxico [2] el código fuente de nuestro programa es fragmentado en bloques de cadenas de texto útiles para las siguientes fases del proceso. Estas unidades fundamentales son conocidas como lexemas.

1.2 Análisis semántico

Este análisis comprende la generación de significado para la diferentes formas de la especificación del lenguaje. Básicamente, provee la infraestructura para analizar el significado de una sentencia, bloque de código, uso correcto de tipos, entre otros.

1.3 Análisis sintáctico

En esta fase se lleva a cabo el análisis del código que hemos escrito y validar que cumpla las reglas de todas las construcciones del lenguaje (esquema de palabras clave o reservadas, uso de operadores, separadores de sentencias, &c.). Esta fase producirá el árbol sintáctico.

1.4 Generación de código intermedio

La generación de código intermedio consiste en producir código para optimización (basado en la plataforma, máquina virtual).

2. Sistema de Tipos

Los lenguajes de programación modernos, claro como C#, cuentan con un sistema de tipos. Este sistema abstrae las restricciones, estado, comportamiento, operaciones de los tipos de datos definidos, y éstos a su vez le dan significado a los datos que manipulan. Sobretodo, el sistema de tipos obliga a que las manipulaciones sobre los datos sigan las reglas establecidas sobre el tipo de dato [2].

Los lenguajes de programación que cuentan con un sistema de tipos, pueden llevar a cabo esta tarea, ya sea estáticamente, o dinámicamente.

[Nota: Recomiendo la lectura de mi artículo Seguridad de Tipos en C# para conocer más sobre este sistema.]

3. Modos de Comprobación de Tipos

3.1 Estático

Un lenguaje de programación que implemente el sistema de tipos de manera estática, ejecuta todas las rutinas de comprobación de tipos en tiempo de compilación. Cualquier error directo o potencial es detectado antes de generar el programa (código) objeto. Sin embargo, hay que agregar que dependiendo de la perspicacia del compilador, algunos errores pueden pasar por alto, y ser sólo detectados en tiempo de ejecución.

El modo estático para el sistema de tipos, agrega una capa de protección para la detección proactiva de estos errores [2]. Adicionalmente, la verificación en los lenguajes de programación puede variar conociendo que sean fuerte o débilmente tipeados.

3.1.1 Tipeo fuerte

C# es un lenguaje de programación que cabe en esta categoría. Las comprobaciones de tipos se realizan en tiempo de compilación. Por ejemplo, si en una supuesta expresión aritmética como:

"1" + 1

se genera un error en tiempo de compilación:

error CS0029: Cannot implicitly convert type `string' to `int'

Lo que requiere un tratamiento conocido como conversión. Será una conversión explícita que transforme la cadena "1" en un valor numérico computable. Por ejemplo esta última cadena a entero de 32 bits (int) podemos utilizar el método ToInt32 [6] de la clase Convert [5]:

Convert.ToInt32( "1" ) + 1

También en esta categoría caben los operadores de conversión explícitos, como por ejemplo:

(Triangulo) figura;

Otros lenguajes de programación que siguen este mismo paradigma:
  • Java
  • F#

3.1.2 Tipeo débil

Corresponde con los lenguajes de programación que no distinguen de tipos de datos y llevan a cabo las operaciones (detrás de cámara) que asumen y entienden de qué se trata la expresión, por ejemplo la expresión "1" + 1 en Perl [7] es completamente válida, y resultará en 2.

3.2 Dinámico

Cuando hablamos de comprobación de tipos dinámico, nos referimos a las operaciones del sistema de tipos que son llevadas en tiempo de ejecución (anglicismo: runtime). Todas las verificaciones (capacidades, restricciones, operaciones, estado, &c.) de un tipo, se operan en tiempo de ejecución mientras nuestra aplicación se ejecuta. Esto ofrece la garantía frente a pruebas reales del flujo de ejecución de las aplicaciones que estemos próximos a lanzar. En  consecuencia, nos da la garantía (cuasi 100%) de que cuando la aplicación entre en producción  estará libre de bugs (errores).

Entre las utilidades de la comprobación de tipos dinámico, tenemos [2]:
  • Fácil y eficiente testing unitario.
  • Para scripting multiplataforma
  • Runtime reflection [8]
Ejemplos de lenguajes de programación de esta categoría [2]:
  • Python
  • Javascript
  • PHP
  • Perl
  • Lisp
  • Ruby

3.2.1 Dinamismo en la plataforma .NET

En el Framework .NET se cuenta con la infraestructura (librerías, construcciones de lenguaje, &c.) necesaria para alcanzar un verdadero dinamismo [2]. Aunque este dinamismo puede lograr por diferentes medios, aquí trataré con reflection [8][9].

Veamos un ejemplo para comprender mejor este concepto.

Archivo ClaseFactorial.cs:



Esta clase sólo tiene construcciones elementales de una clase. (Nuevo término: POCO - Plain Old C# Object: una clase con definiciones elementales.)

A través de reflection podemos introducir el dinamamismo utilizando construcciones avanzadas para la creación de una de instancia de la clase ClaseFactorial y la consecuente ejecución del método ComputarFactorial. Así:

Assembly assem = Assembly.GetExecutingAssembly();
object factorial = Activator.CreateInstance( assem.GetType ("ClaseFactorial"));
Type tipoFactorial = factorial.GetType();
MethodInfo metodoFactorial = tipoFactorial.GetMethod("ComputarFactoral");
object resultado = metodoFactorial.Invoke (factorial, new object[] {10});
int resultadoFactorial = Convert.ToInt32 (resultado);
Console.WriteLine (resultadoFactorial);

3.2.2 Dynamic Language Runtime (DLR)

En [2] se explicita:

"The DLR (Dynamic Language Runtime) adds a set of libraries which makes doing the above much easy and fun. It adds a set of features designed specifically to meet the needs of dynamic languages such as a shared dynamic type system, a common hosting model and abilities to generate fast dynamic code and fast symbol tables."
Además, la DLR está disponible desde la versión 4.0 del Framework .NET. En la Figura 2 [2] se muestra cómo está integrada la DLR sobre la Common Language Runtime (CLR).
DLR soportada por la Common Language Runtime (CLR)
Figura 2. DLR soportada por la Common Language Runtime (CLR) [2].
Ahora observemos en la Figura 3 [2] cómo DLR permite la interoperabilidad con múltiples lenguajes de programación compatibles con .NET:
Soporte de .NET para DLR.
Figura 3. Soporte de .NET para DLR [2].

4. Conclusiones

Grosso modo vimos el ciclo de vida del proceso de compilación. De aquí, precisamente desde la fase de Análisis Sintáctico, extrajé el concepto de sistema de tipos el cual nos proporcioná los mecanismos para hacer validaciones sobre los tipos que usamos para manipular datos. Aprendimos que este sistema funcionada en modo estático y dinámico; en el caso de éste último, observamos que el Framework .NET provee los mecanimos necesarios para soportar programación dinámica. Al final vimos como el DLR está soportado por la CLR y el Framework .NET.

5. Glosario

  • Compile time
  • Dinamismo
  • DLR
  • Perl
  • PHP
  • Reflection
  • Ruby
  • Runtime

6. Enlaces & Literatura

[1]: C# 5.0 in a Nutshell by Joseph Albahari and Ben Albahari. Copyright 2012 Joseph Albahari and Ben Albahari, 978-1-449-32010-2.
[2]: Infosys.NET Framework - dynamic-language-runtime.pdf - http://www.infosys.com/microsoft/resource-center/Documents/dynamic-language-runtime.pdf
[3]: The Type System - http://www.pearsonhighered.com/assets/hip/us/hip_us_pearsonhighered/samplechapter/0201770180.pdf
[4]: Compiler, the free encyclopedia - https://en.wikipedia.org/wiki/Compiler
[5]: Convert Class (System) - http://msdn.microsoft.com/en-us/library/vstudio/system.convert%28v=vs.100%29.aspx
[6]: Convert.ToInt32 Method (System) - http://msdn.microsoft.com/en-us/library/vstudio/system.convert.toint32%28v=vs.100%29.aspx
[7]: Perl, the free encyclopedia - https://en.wikipedia.org/wiki/Perl
[8]: Reflection (computer programming), the free encyclopedia - https://en.wikipedia.org/wiki/Reflection_%28computer_programming%29
[9]: Reflection in the .NET Framework - http://msdn.microsoft.com/en-us/library/f7ykdhsy%28v=vs.110%29.aspx


J

No hay comentarios:

Publicar un comentario

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