jueves, 12 de junio de 2014

Receta No. 3-17 en C#: Invocación Dinámica de Miembros de un Tipo

Tabla de Contenido

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
3.1 dynamic
4. Práctica: Código Fuente C#
5. Conclusiones
6. Glosario
Literatura & Enlaces

0. Introducción

Esta receta nos presentará una alternativa a reflection a través del uso de la construcción dynamic. Conoceremos los escenarios en donde es apropiado su uso, y también, cuando debemos obviar su uso por asuntos de pérdida del rendimiento de una aplicación, y en particular, por la pérdida de la comprobación estática de tipos.

1. Problema

En la receta 1-19 (Creación de un Tipo Dinámico usando ExpandoObject) aprendimos acerca del uso de la construcción dynamic para la representación de tipos de forma dinámica, es decir, en tiempo de ejecución. Ahora que ya sabemos de su existencia en C#, queremos aplicarla para invocar miembros de un tipo de forma dinámica.

2. Solución

La solución a este requerimiento consiste en la conjugación del conocimiento y práctica adquiridos en la receta anterior (Invocar un Miembro de un Tipo con Reflection), donde aprendimos cómo invocar los miembros de un tipo usando la técnica .NET Reflection: uso del método InvokeMember (System.Type) y el método Invoke (System.Reflection.MemberInfo), y el uso de la construcción dynamic.

3. Discusión de la solución

Gracias a [1] sabemos que el uso de la construcción dynamic [3] de C# representa un enfoque alternativo a reflection, esto, para la invocación de miembros de un tipo usando el soporte con el que cuenta la versión 4.0 de .NET 4.0: DLR (Dynamic Language Runtime) [4].

Veamos en breve la definición uso de dynamic.

3.1 dynamic

Desde la versión 4.0 de .NET en C# podemos utilizar dynamic para la resolución de operaciones sobre tipos en tiempo de ejecución. Mientras que en tiempo de compilación, la comprobación de tipos, casting, boxing y unboxing, &c., son descartados por el compilador.

En este ejemplo podemos encontrar la diferencia entre la comprobación en tiempo de compilación y en tiempo de ejecución.

Archivo de código fuente PruebaDynamicObject.cs [enlace alternativo]:

En la línea 10 declaramos una variable de dynamic -dyn-, y la inicializamos con la literal entera 1;en la siguiente línea una instancia de object -obj-, con el valor entero 1 (boxing). Más adelante sobre la línea 13 sumamos 3 al valor 1 que tiene encapsulado el tipo dynamic (línea 10).

Continuando, si intentamos hacerlo mismo, es decir 

obj = ((object)obj) + 3;


El compilador de C# generará el error CS0019 [5] debido a que no es posible aplicar el operador + sobre un tipo object y una literal entera (en su lugar, era necesario hacer casting((object)obj) + 3). Notemos que esto ha ocurrido en tiempo de compilación, y por supuesto, nos genera una ventaja significativa sobre la comprobación de tipos en tiempo de ejecución. Demostrémolo a través del intento de invocar un miembro no definido sobre la variable dynamic -dyn-:

dyn.MetodoInexistente ()

La compilación del programa en el archivo de código fuente PruebaDynamicObject.cs no generará ningún error en tiempo de compilación, sin embargo, cuando intentemos ejecutar el assembly:

  1. .\PruebaDynamicObject.exe

obtendremos el siguiente mensaje de error:
Intento de invocar método inexistente en una variable dynamic
Figura 1. Intento de invocar método inexistente en una variable dynamic.
En el mensaje de error que aparece en la Figura 1 se informa que ha ocurrido la excepción RuntimeBinderException (N:Microsoft.CSharp.RuntimeBinder) [6] que se traduce en el intento de enlazar (binding) miembros de un tipo inexistentes en tiempo de ejecución.

Antes de finalizar, sobre el artículo en DotNetPerls [2, 7] nos advierten sobre el uso de dynamic:
Dynamic is advanced functionality. It can be useful. But usually it should be avoided. It erases many benefits of the C# language.
Además:
The dynamic keyword influences compilation. A dynamic variable, parameter or field can have any type. Its type can change during runtime. The downside is that performance suffers and you lose compile-time checking.
[Nota: Recomiendo la lectura de la receta Creación de un Tipo Dinámico usando ExpandoObject en donde podrán encontrar una explicación más extensa sobre este tipo .]

4. Práctica: Código Fuente C#

En este ejemplo crearemos la versión con uso de dynamic en contraste con la versión de la sección práctica de la receta 3.16 [9]:

Archivo de código fuente VersionDynamic.cs [enlace alternativo]:
Empezando la sección de código cliente (líneas 15-27), exactamente en la línea 17 declaramos la variable dynamic -dyn-. En la línea 20, asignamos a esta variable una instancia de la clase VersionDynamic. Invocamos al método ImprimirMensaje con los parámetros formales de su firma. En esta última parte debemos asegurarnos que el miembro que estamos invocando existe, y que los argumentos son aplicados tal cual como se especifican la firma (tipos y orden).

Compilación:


  1. csc /target.exe VersionDynamic.cs

Ejecución assembly:


  1. .\VersionDynamic.cs

Resultado:
Prueba ejecución de receta con uso de dynamic
Figura 2. Prueba ejecución de receta con uso de dynamic.

5. Conclusiones

Creamos una receta en la que aprendimos a invocar miembros de un tipo de forma dinámica a través del uso de la construcción dynamic (que fue introducida en la versión 4.0 de .NET Framework). En la sección práctica se demostró que invocar miembros de un tipo es mucho más simple que utilizar la técnica .NET Reflection, sin embargo, como se menciono dynamic empobrece el rendimiento de una aplicación, además de disuadir la comprobación de tipos en tiempo de compilación.

6. Glosario

  • Comprobación de tipos
  • Dinámico
  • DLR
  • Miembro de tipo
  • Reflection
  • Tiempo de compilación
  • Tiempo de ejecución

7. Literatura & Enlaces

[1]: Visual C# 2010 Recipes by Allen Jones and Adam Freeman. Copyright 2010 Allen Jones and Adam Freeman, 978-1-4302-2525-6.
[2]: C# Dynamic Keyword - http://www.dotnetperls.com/dynamic
[3]: dynamic (C# Reference) - http://msdn.microsoft.com/en-us/library/dd264741.aspx
[4]: Dynamic Language Runtime Overview - http://msdn.microsoft.com/en-us/library/dd233052(v=vs.110).aspx
[5]: Compiler Error CS0019 - http://msdn.microsoft.com/en-us/library/a63h61ky.aspx
[6]: RuntimeBinderException Class (Microsoft.CSharp.RuntimeBinder) - http://msdn.microsoft.com/en-us/library/microsoft.csharp.runtimebinder.runtimebinderexception(v=vs.110).aspx
[7]: C# Tutorial: Dot Net Perls - http://www.dotnetperls.com/
[8]: Receta No. 1-19 en C#: Creación de un Tipo Dinámico usando ExpandoObject | OrtizOL Experiencias Construcción Software - http://ortizol.blogspot.com/2014/02/receta-no-1-19-en-c-creacion-de-un-tipo.html
[9]: Receta No. 3-16 en C#: Invocar un Miembro de un Tipo con Reflection | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/06/receta-no-3-16-en-csharp-invocar-un-miembro-de-un-tipo-con-reflection.html


J

No hay comentarios:

Publicar un comentario

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