martes, 19 de octubre de 2010

Reflection y cómo ser un Jedi (Part 1)

¿Qué es reflection?

Respuesta wikipedia: "Reflection es el proceso por el cual un programa puede observar y modificar su propia estructura o comportamiento en tiempo de ejecución."

Respuesta maestro Jedi: "reflection es el control de la fuerza."

Respuesta serie Numb3rs: "Usamos reflection todos los días, al usar el IntelliSense, para debuggar, al usar contenedor IoC. También lo usamos para añadir webparts, para inspeccionar el código y para predecir su comportamiento. Con reflection podemos solucionar los misterios que se nos planteen."

¿Qué debemos hacer para empezar a utilizar la fuerza?

Existe un namespace llamado System.Reflection y una clase conocida como System.Type que nos acompañaran, en esta, fabulosa aventura hacia el interior del código.

Y así, sin más, ¿puedo empezar a usarlo?

Claro, pero no esperes aquí una clase magistral… <tono de Bilbao> ¡¡que somos desarrolladores joder!!</tono de Bilbao>. Dame un  punto de apoyo  HelloWorld y moveré el mundo conoceré toda la sintaxis.

Así que aquí van unas cuantos ejemplos, para la explicación vamos a utilizar una clase estúpida dentro de un namespace estúpido.

namespace StupidNameSpace
{
public class StupidClass
{
private string _field1;
private string _field2;

public StupidClass()
{
}

public StupidClass(string field1, string field2)
{
_field1 = field1;
_field2 = field2;
}

public string Field1
{
get { return _field1; }
set { _field1 = value; }
}

public string Field2
{
get { return _field2; }
set { _field2 = value; }
}

public string FooMethod1()
{
return string.Empty;
}

public string FooMethod2(string parameter)
{
return parameter;
}
}


Ahora vamos a crear una pequeña aplicación de consola para interrogar, utilizando la fuerza (reflection), a nuestra clase.

Ejemplo 1:
En el primer ejemplo vamos a crear un objeto de tipo StupidClass, usando el constructor público sin parámetros y luego utilizando el constructor con parámetros.



using System;
using System.Reflection;

namespace StupidNameSpace
{
public class Program
{
static void Main(string[] args)
{
// Obtenemos el tipo, tenemos que pasadle todo el fully qualified name,
// tened en cuenta que estamos en el mismo namespace, y en el mismo assembly, sino fuera así,
// tendriamos que pasarle, el nombre del assembly.
var type = Type.GetType("StupidNameSpace.StupidClass");
// Podriamos haber usado:
// var type = typeof(StupidClass);

// Usamos el constructor sin parámetros (default constructor) para crear un objecto llamado
// myStupidObject
var types = new Type[0];
var constructorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, types, null);
var myStupidObject = (StupidClass)constructorInfo.Invoke(null);

// Ahora hacemos lo mismo pero llamamos al constructor que recibe 2 parámetros
// y creamos un objeto llamado myStupidObject2
types = new Type[2];
types[0] = typeof (string);
types[1] = typeof (string);

var parameters = new object[2];
parameters[0] = "param1";
parameters[1] = "param2";

constructorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, types, null);
var myStupidObject2 = (StupidClass) constructorInfo.Invoke(parameters);
}
}
}


Esto esta muy bien, pero porque sabemos que constructor tiene la clase, si es público o privado, si recibe parámetros, etc. Pero y si no lo sabemos. Pues preguntémosle.



using System;
using System.Reflection;

namespace StupidNameSpace
{
public class Program
{
static void Main(string[] args)
{
// Obtenemos el tipo, tenemos que pasadle todo el fully qualified name,
// tened en cuenta que estamos en el mismo namespace, y en el mismo assembly, sino fuera así,
// tendriamos que pasarle, el nombre del assembly.
var type = Type.GetType("StupidNameSpace.StupidClass");

ConstructorInfo[] constructors = type.GetConstructors();
foreach(ConstructorInfo ci in constructors)
{
Console.WriteLine("===========");
Console.WriteLine("CONSTRUCTOR");
Console.WriteLine("===========");
Console.WriteLine(ci.Name);
if(ci.IsPrivate) Console.WriteLine("Private");
else Console.WriteLine("Public");
if(ci.IsStatic) Console.WriteLine("Static");
if(ci.IsAbstract) Console.WriteLine("Abstract");
if (ci.IsVirtual) Console.WriteLine("Virtual");
var parameters = ci.GetParameters();
if(parameters.Length > 0) Console.WriteLine("Parameters:");

foreach(ParameterInfo pi in parameters)
{
Console.WriteLine("-->" + pi.Name);
Console.WriteLine("-->" + pi.ParameterType);
}
}

Console.Read();
}
}
}

No hay comentarios: