viernes, 6 de junio de 2014

Receta No. 3-12 en C#: Crear un Objeto con Reflection

Tabla de Contenido

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución Conclusiones
4. Práctica: Código Fuente C#
4.1 Instanciar un objeto StringBuilder
4.2 Factoría de plugins
5. Conclusiones
6. Glosario
7. Literatura & Enlaces

0. Introducción

Esta doceava receta nos enseñará el proceso requerido para instanciar objetos en tiempo de ejecución usando una técnica que es conocida como Reflection. A través de reflection podemos conocer la meta-información asociada a un tipo, de un assembly, etc., esto será útil nuestra aplicación se haya en ejecución. Recurriremos a varios de los artefactos localizados en los namespace System y System.Reflection para lograr nuestro objetivo: crear una instancia de un tipo de los assemblies de Microsoft .NET Framework y los de código personalizado del usuario.

1. Problema

Queremos acercarnos al uso de reflection en nuestros desarrollos. Por eso hemos pedido al equipo de formación que nos indique el proceso necesario para crear instancias de un tipo de objeto en tiempo de ejecución.

2. Solución

El equipo del departamento de formación de Ad Infinitum Systems. Nos ha entregado un esquema abstracto de trabajo sobre C# para empezar a investigar y realizar primeras pruebas con el concepto de reflection. Aquí está:
  1. Obtención de la representación Type de un objeto.
  2. Invocación del constructor que nos interese a través del método GetConstructor. para la obtención de una instancia de ConstructorInfo (N:System.Reflection).
  3. Invocación del método ConstructorInfo.Invoke.

3. Discusión de la solución

3.1 Introducción a Reflection

De acuerdo con [4], reflection es una técnica que consiste en la investigación o consulta de información de un tipo, assembly, módulo, &c., en tiempo ejecución. Esto nos abre la posibilidad de manipular los miembros (métodos, propiedades, constructores, eventos, etc.) de un tipo de objeto con técnicas conocidas de programación.

Con el siguiente ejemplo queda mejor ilustrado el concepto de reflection:

En la línea 4 creamos una clase concreta Calculadora y creamos el método Sumar (líneas 8-11) en su cuerpo de declaración. Desde código cliente (líneas 16-34), creamos un instancia de esa Calculadora; luego a través del método heredado de Object, GetType, obtenemos la representación Type [5] de ese objeto. A continuación, solicitamos la obtención del método -infoTipo.GetMethod("Sumar")-, y lo invocamos con el conjunto de argumentos especificados en el arreglo object infoMetodo.Invoke(calc, args) (línea 33).

> Prueba de ejecución.

Resultado:

Reflection: MethodInfo
Nombre tipo: Recetas.Cap03.Calculadora
 Retorno: 10

3.2 Creación objetos con reflection

A partir de lo que comprendimos en la sección anterior, vamos a extender los pasos requeridos para la creación de una instancia de un objeto en tiempo de ejecución utilizando la técnica reflection. Estos son los pasos:

Paso 1: Obtención de la representación Type del objeto


En la Figura 1 se ilustra (a modo de resumen) los métodos de obtención de la representación Type para cualquier tipo de objeto.

Métodos de obtención representación Type
Figura 1. Métodos de obtención representación Type.

En la receta 3.10 (Obtención de Información de Tipos (Reflection)) podrán encontrar más detalle acerca de este proceso.

Paso 2: Invocar método GetConstructor


El método GetConstructor posee una lista de versiones sobrecargadas [7]. Utilizaremos, para ejemplificar, la versión que recibe un arreglo de objetos Type como argumento. Esta es su firma [8]:

public ConstructorInfo GetConstructor(Type[] types)

Este es un ejemplo (recuperado y adaptado de [8]):

Archivo UsoTypeGetConstructor.cs [enlace alternativo]:

En línea 15 obtenemos la representación Type para la clase UsoTypeGetConstructor; más adelante, en la línea 18 obtenemos una instancia de ConstructorInfo que hace referencia al constructor de UsoTypeGetConstructor con un parámetro int. Al método GetConstructor se le pasa el arreglo de elementos Type con la representación Type para int. En caso que la invocación

tipo.GetConstructor(new Type[] {typeof(int)});

retorne un valor diferente a null, se ejecutará la sentencia de la línea 22.



Resultado:

Constructor con firma que contiene un parámetro `int`: Void .ctor(Int32)

Paso 3: Invocación del método Invoke de ConstructorInfo


El método Invoke [9] se encarga de invocar el constructor a una variable ConstructorInfo. La versión sobrecargada [10] permite especificar los argumentos para el constructor referenciado por una instancia de ConstructorInfo. Firma de este último:

public Object Invoke(Object[] parameters)


Modificamos el archivo de código fuente UsoTypeGetConstructor.cs [enlace alternativo]:
En la línea 22 invocamos al constructor con la lista de argumentos especificada con:

new object[] {13}


para el argumento del método Invoke. Hacemos el casting para obtener la instancia del tipo correspondiente:

(UsoTypeGetConstructor)



Resultado:

Blog xCSw

3.3 Aplicaciones de reflection

En [1] enuncian una aplicación de la técnica reflection:
Reflection functionality is commonly used to implement factories in which you use reflection to instantiate concrete classes that either extend a common base class or implement a common interface. Often, both an interface and a common base class are used. The abstract base class implements the interface and any common functionality, and then each concrete implementatation extends the base class.
 En el segundo ejemplo de la sección práctica crearemos una aplicación con esta característica.

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

4.1 Instanciar un objeto StringBuilder

En este primer ejemplo vamos a utilizar reflection para crear un objeto StringBuilder.

Archivo CreacionStringBuilder.cs [enlace alternativo]:

En la línea 25 obtenemos la representación Type de StringBuilder [11] con el uso del operador typeof. Con la sentencia 

Type[] argsTipos = new Type[] { typeof (String), typeof(Int32)};


en la línea 29 especificamos la lista de los tipos de los parámetros para un constructor de StringBuilder ([12]). La variable argsTipos la pasamos como argumento al método GetConstructor (línea 32) para obtener la información del constructor sobre una instancia ConstructorInfo. Los argumentos para la firma del constructor recién creado los especificamos en un arreglo object en la línea 35. Finalmente, ya podemos crear la instancia invocando al método Invoke en la sentencia de la línea 38. El código cliente (líneas 9-20) comprende el uso de una instancia de StringBuilder.

Compilación:


  1. csc /target.exe CreacionStringBuilder.cs

> Prueba de ejecución.

Resultado:

Blog xCSw

4.2 Factoría con reflection

El siguiente ejemplo es una adaptación del hallado en [1], el cual consiste en la creación de una factoría (creación de instancias) de objetos IPlugin.

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

Compilación:


  1. csc /target.exe Factoria.cs

Ejecución:


  1. .\Factoria.exe

Resultado:
Resultado ejecución ejemplo de factoría de plugins
Figura 2. Resultado ejecución ejemplo de factoría de plugins.

5. Conclusiones

La preparación de esta receta en C# nos ha enseñado los básicos para la creación de un objeto utilizando la técnica reflection. Esto es de bastante utilidad para crear objetos en tiempo de ejecución. También podemos manipular los tipos pertenecientes a un assembly y crear instancias según sea necesario (lo aprendimos con los ejemplos presentados en la sección 4).

6. Glosario

  • Assembly
  • Constructor
  • ctor
  • Factoría
  • Instancia
  • Objeto
  • Plugins
  • Reflection

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]: Reflection in C# Tutorial - CodeProject - http://www.codeproject.com/Articles/17269/Reflection-in-C-Tutorial
[3]: Reflection (C# and Visual Basic) - http://msdn.microsoft.com/en-us/library/ms173183.aspx
[4]: Reflection in .NET - CodeProject - http://www.codeproject.com/Articles/55710/Reflection-in-NET
[5]: Type Class (System) - http://msdn.microsoft.com/en-us/library/system.type(v=vs.110).aspx
[6]: Receta No. 3-10 en C#: Obtención de Información de Tipos (Reflection) - http://ortizol.blogspot.com/2014/06/receta-no-3-10-en-csharp-obtencion-de-informacion-de-tipos-reflection.html
[7]: Type.GetConstructor Method (System) - http://msdn.microsoft.com/en-us/library/System.Type.GetConstructor(v=vs.110).aspx
[8]: Type.GetConstructor Method (Type[]) (System) - http://msdn.microsoft.com/en-us/library/h93ya84h(v=vs.110).aspx
[9]: ConstructorInfo.Invoke Method (System.Reflection) - http://msdn.microsoft.com/en-us/library/system.reflection.constructorinfo.invoke(v=vs.110).aspx
[10]: ConstructorInfo.Invoke Method (Object[]) (System.Reflection) - http://msdn.microsoft.com/en-us/library/6ycw1y17(v=vs.110).aspx
[11]: StringBuilder Class (System.Text) - http://msdn.microsoft.com/en-us/library/system.text.stringbuilder(v=vs.110).aspx
[12]: StringBuilder Constructor (String, Int32) (System.Text) - http://msdn.microsoft.com/en-us/library/zb91weab(v=vs.110).aspx


M

No hay comentarios:

Publicar un comentario

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