viernes, 13 de junio de 2014

Receta No. 3-18 en C#: Cómo Crear un Tipo Dinámico Personalizado

Tabla de Contenido

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
3.1 Clase DynamicObject
3.1.1 Método TryGetMember
3.1.2 Método TrySetMember
3.2 Ejemplo de herencia de DynamicObject
4. Práctica: Código Fuente C#
5. Conclusiones
6. Glosario
7. Literatura & Enlaces

0. Introducción

Esta última receta de la serie dominios de aplicación, reflection, y metadatos, aprenderemos cómo crear un tipo personalizado de forma dinámica: establecimiento y recuperación de miembros (propiedades). Usaremos una de las clases introducidas en DLR (.NET Framework 4.0): DynamicObject. Esta clase nos proveerá la maquinaria necesaria para la agregación de propiedades, métodos, índices, &c. a un tipo en tiempo de ejecución. (Los ejemplos mostrados representan una introducción a DLR, que desde luego, será extendida en futuros artículos y/o recetas.)

1. Problema

Ahora damos a paso a un nivel más abstracto y exigente: requerimos de un método que nos permita la adición de comportamiento personalizado a un tipo, y que en consecuencia podamos invocar sus miembros de forma dinámica.

2. Solución

El nombre de espacios System.Dynamic (que hace parte de .NET Framework y que principalmente agrega funcionalidades dinámicas de lenguaje a la CLR) contiene la clase base DynamicObject que nos va a permitir especificar comportamiento dinámico en tiempo de ejecución. Esta clase tendremos que extenderla y sobrescribir los métodos virtual TryXXX (XXX para referirse a los postfijos de la nomenclatura).

3. Discusión de la Solución

3.1 Clase DynamicObject


La clase DynamicObject, es la clase base que facilita la especificación de comportamiento dinámico en tiempo de ejecución para los tipos derivados de esta. No es posible crear instancias directas de esta clase, para lograrlo tendremos que crear una jerarquía de herencia y crear subtipos que hereden de DynamicObject y sobrescribir varios de sus miembros que inician con el prefijo Try (en la Figura 1 [1] se enumeran varios de estos métodos).
Métodos virtual de la clase DynamicObject
Figura 1. Métodos virtual de la clase DynamicObject.
De acuerdo con la documentación de esta clase en [2] es importante incluir (literalmente) este comentario:
"The DynamicObject class enables you to define which operations can be performed on dynamic objects and how to perform those operations. For example, you can define what happens when you try to get or set and object property, call a method, or perform standard mathematical operations such as addtion and multiplication."
Además, esta nota:
Nota sobre el uso de ExpandoObject y IDynamicMetaObjectProvider
Figura 2. Nota sobre el uso de ExpandoObject y IDynamicMetaObjectProvider [2].

Adicionalmente, el hecho de contar con tipos personalizados que deriven de esta clase nos va a permitir crear definiciones de código más intuitivas para los clientes del tipo concreto, por ejemplo:

En lugar de tener una definición como esta

obj.SetProperty ("Contador", 1);

los métodos sobrescritos de DynamicObject en una subclase, facilitan este medio 

obj.Contador = 1;

Desde luego que para nosotros esta última definición es mucho más intuitiva, pues resulta más simple y directa en cuanto a especificación sintáctica (como las propiedades -cfr. Propiedades en C#-)

Para lograr este grado de afinidad sintáctica tendremos que sobrescribir los métodos:
  • TryGetMember
  • TrySetMember

3.1.1 Método virtual TryGetMember

El método TryGetMember [6] se invoca implícitamente cuando intentamos obtener un valor por medio de una propiedad. En la versión sobrescrita en la subclase (herencia de DynamicObject) se especifica el comportamiento dinámico para las operaciones de obtención de valores. Esta es su firma [5]:

public virtual bool TryGetMember(GetMemberBinder binder, out Object result)

Descripción puntual:
  • Parámetros:
    • binder: es el generador de la información del objeto que invoca el comportamiento dinámico.
    • result: resultado de la operación de la operación de obtención de valor (debe establecer un valor independiente de si se ha encontrado un valor o no, esto debido a que su especificación incluye el modificador out).
  • Retorno: true si la operación de establecimiento de valor fue satisfactoria, false en caso contrario. (En este último caso se suelen lanzar excepciones.)

3.1.2 Método virtual TrySetMember

El método TrySetMember [5] permite especificar las operaciones que se han de llevar a cabo para el establecimiento de valores de un miembro. Este método puede ser sobrescrito por el tipo derivado de DynamicObject, y esencialmente se usa para especificar el comportamiento dinámico que de ejecutarse al cambiar el valor de una propiedad. Esta es su firma:

public virtual bool TrySetMember(SetMemberBinder binder, Object value)

Descripción puntual:
  • Parámetros:
    • binder: es el generador de la información del objeto que invoca el comportamiento dinámico.
    • value: instancia de object con la información a establecer sobre el miembro (e.g., propiedad)
  • Retorno: true si la operación de establecimiento de valor fue satisfactoria, false en caso contrario.

3.2 Ejemplo de herencia de DynamicObject

Para facilitar la comprensión del uso de la clase DynamicObject, he decidido hacer una adaptación de código fuente del ejemplo presentado por Anoob Madhusudanan en su artículo acerca de las capacidades dinámicas de .NET 4.0 en [6]:

Archivo de código fuente ClaseDinamica.cs [enlace alternativo]:
En la línea 7 especificamos que la clase ClaseDinamica hereda directamente desde DynamicObject. Creamos una instancia de la colección clave/valor Dictionary<TKey, TValue> [7] para almacenar los miembros que agregamos dinámicamente a una instancia de la clase dinámica ClaseDinamica.

Continuando, en las líneas 19-30 sobrescribimos el método TryGetMember para recuperar el valor de un miembro agregado dinámica. La operación contraria ocurre con el método sobrescrito TrySetMember (líneas 38-51). Con el método TryInvokeMember (líneas 60-71) invocamos un miembro (delegado) utilizando las capacidades dinámicas heredadas de la clase DynamicObject. Más adelante, en las líneas 77-80, sobrescribimos el método GetDynamicMemberNames para descubrir las miembros agregados en tiempo de ejecución a la instancia de ClaseDinamica.


En la región de código cliente, líneas 82-103, definimos una variable del tipo dynamic (cfr. Invocación Dinámica de Miembros de un Tipo) con el identificador -dyn-. Envolvemos una instancia (línea 88) de ClaseDinamica sobre la variable anterior. Más adelante, en la línea 82 creamos de forma dinámica la propiedad Contador y le asignamos el valor literal entero 13. (Para este caso se ha invocado implícitamente el método TrySetMember).



Por otro lado, sobre la línea 95 creamos un delegado a partir del delegado genérico Action<T> (cfr. Tipos Genéricos en C# - Parte 8: Delegados Genéricos) con la expresión lambda texto => Console.WriteLine (texto). A continuación, en la línea 98, lo asignamos al miembro EscribirEnConsola. Para finalizar, invocamos el delegado recién creado y asignado y le pasamos como argumento la representación string de la propiedad dinámica Contador.

Compilación:


  1. csc /target:exe ClaseDinamica.cs

Ejecución assembly:


  1. .\ClaseDinamica.exe

Resultado:
Prueba de ejecución de instancia de la clase dinámica ClaseDinamica
Figura 2. Prueba de ejecución de instancia de la clase dinámica ClaseDinamica.

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

Sección idónea para que afiancemos el entendimiento sobre las potencialidades de la herencia de DynamicObject. Creamos la herencia correspondiente, y agregaremos miembros de forma dinámica a una instancia de la clase hija (subclase).

Archivo de código fuente DiccionarioDinamico.cs [enlace alternativo]:
Compilación:


  1. csc /target:exe DiccionarioDinamico.cs

Ejecución assembly:


  1. .\DiccionarioDinamico.exe

Resultado:
Prueba de ejecución de instancia de la clase dinámica DiccionarioDinamico
Figura 3. Prueba de ejecución de instancia de la clase dinámica DiccionarioDinamico.

5. Conclusiones

A pesar del grado de complejidad (puede ser subjetivo) del uso de DynamicObject, hemos obtenido un conocimiento básico, claro, y conciso de esta clase. En los ejemplos prácticos quedó demostrado la capacidad dinámica sobre los miembros de tipo que introduce el uso conjugado del tipo dynamic y la instanciación de una subclase DynamicObject.

Glosario

  • CLR
  • Dinámico
  • DLR

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]: DynamicObject Class (System.Dynamic) - http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject(v=vs.110).aspx
[3]: Dynamic Language Runtime Overview - http://msdn.microsoft.com/en-us/library/dd233052(v=vs.110).aspx
[4]: Propiedades en C# | OrtizOL - Experiencias Construcción Software (xCSw) http://ortizol.blogspot.com/2013/10/propiedades-en-c.html
[5]: DynamicObject.TryGetMember Method (System.Dynamic) - http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.trygetmember(v=vs.110).aspx
[6]: Adventures with C# 4.0 dynamic - ExpandoObject, ElasticObject, and a Twitter client in 10 minutes - CodeProject - http://www.codeproject.com/Articles/62839/Adventures-with-C-dynamic-ExpandoObject-Elasti
[7]: Dictionary(TKey, TValue) Class (System.Collections.Generic) - http://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx
[8]: Tipos Genéricos en C# - Parte 8: Delegados Genéricos - http://ortizol.blogspot.com/2014/05/tipos-genericos-en-c-parte-8-delegados.html


J

No hay comentarios:

Publicar un comentario

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