viernes, 4 de febrero de 2011

[C#] Instancias en runtime de tipos dinámicos

 

Durante el desarrollo de algunas herramientas es necesario generalizarlas para poder reutilizarlas posteriormente, pero manteniendo siempre una funcionalidad concreta. En el caso que nos ocupa, quiero poder listar una serie de atributos genéricos y poder operar con ellos en la precisión deseada, pero partiendo de la premisa anterior. Es decir, no tendré una clase que disponga de algo similar:

  1. public class A
  2.         {
  3.             int a;
  4.             float b;
  5.             int c;
  6.             UInt16 x;
  7.         }

Esto es muy costoso de mantener en futuros proyectos, puesto que estaría obligado a cambiar manualmente todos los atributos del objeto y sus nombres pese a que el comportamiento puede estar definido, puesto que sólo me interesa poder operar con los números.

¿Solución? Pues muy sencillo. Por un lado, vamos a almacenar únicamente el  nombre de atributo junto con su tipo y valor, todo con string. Posteriormente usaremos reflection para cargar el tipo de forma dinámica. Hasta aquí todo es correcto, nuestro mayor problema será que el evidentemente esos objetos dinámicos no son tipados y el compilador no sabrá qué son salvo que lo especifiquemos explícitamente mediante un casting. Es por ello que aquí entran los tipos dinámicos.

Los tipos dinámicos (o dynamic type, en inglés) es una nueva feature de C# 4.0. Recomiendo la lectura de la documentación para no caer en el error de confundirlos con los tipos anónimos. Debemos tener en cuenta que lo que queremos es operar con ellos en tiempo de ejecución, por lo que toda operación no soportada en tiempo de compilación por un tipo no nos servirá. De ahí que los tipos anónimos no sirva para el siguiente ejemplo, ya que no puedo operar entre objetos sin especificar explícitamente qué quiero hacer.

Es por ello que una vez tenemos el tipo deseado a instanciar mediante Reflection, podemos asignarle el valor y el tipo a la variable dinámica. Y podremos operar con ellos como si se tratase de tipos compilados previamente. Veamos un ejemplo:

  1. class Program
  2.     {
  3.         static void Main(string[] args)
  4.         {
  5.             string uno = "1";
  6.             string dos = "2,5";
  7.  
  8.             dynamic a = Convert.ChangeType(uno, Type.GetType("System.Single"));
  9.             dynamic b = Convert.ChangeType(dos, Type.GetType("System.Single"));
  10.  
  11.             Console.Out.WriteLine(a);
  12.             Console.Out.WriteLine(b);
  13.             Console.Out.WriteLine(a+b);
  14.             Console.Out.WriteLine(a-b);
  15.             Console.Out.WriteLine(a*b);
  16.             Console.Out.WriteLine(a/b);
  17.             Console.ReadLine();
  18.         }
  19.     }

A través de Type.GetType() uso Reflection para cargar el tipo que quiero instanciar. Con Convert.ChangeType asigno un valor a un tipo, y todo ello a una variable dinámica. De este modo podremos operar con los números como si se tratasen de floats.