csharp-reflexion

Cómo usar reflexión en C#

La reflexión es el proceso mediante el cual un programa puede inspeccionar y modificar su propia estructura y comportamiento en tiempo de ejecución. En C#, la reflexión se implementa a través del espacio de nombres System.Reflection.

Con la reflexión, se puede obtener información sobre ensamblados, módulos, tipos, métodos, propiedades, campos y eventos, y también se puede invocar métodos o acceder a propiedades de manera dinámica.

Obtener información sobre las variables y tipos de datos

Una de las principales utilidades de la reflexión en C# es la capacidad de obtener información sobre las variables y tipos de datos utilizados en nuestro programa. Podemos obtener detalles como el nombre de la variable, su tipo, su valor actual, entre otros.

Para lograr esto, podemos utilizar la clase Type de C#. Esta clase nos permite acceder a información sobre los tipos de datos utilizados en nuestro código.

Se puede obtener una instancia de Type de varias maneras, como usando el operador typeof, el método GetType o el método estático Type.GetType.

int numero = 10;
Type tipoNumero = numero.GetType();

En este ejemplo, estamos obteniendo el tipo de dato de la variable numero y almacenándolo en la variable tipoNumero.

Ahora podemos usar el Type para acceder al resto de propiedades y elementos de la clase. Voy a poner un ejemplo completo para verlo bien 👇

using System;
using System.Reflection;

public class Ejemplo
{
    public int Campo;
    public void Metodo() { }
}

public class Programa
{
    public static void Main()
    {
        Type tipo = typeof(Ejemplo);
        Console.WriteLine("Nombre del tipo: " + tipo.Name);

        FieldInfo[] campos = tipo.GetFields();
        foreach (var campo in campos)
        {
            Console.WriteLine("Campo: " + campo.Name);
        }

        MethodInfo[] metodos = tipo.GetMethods();
        foreach (var metodo in metodos)
        {
            Console.WriteLine("Método: " + metodo.Name);
        }
    }
}

Modificar variables y tipos de datos

Además de obtener información, también podemos utilizar la reflexión para modificar variables y tipos de datos en tiempo de ejecución. Para acceder y modificar propiedades, se utilizan PropertyInfo.

Por ejemplo, supongamos que tenemos una clase Persona con una propiedad Nombre y queremos cambiar su valor en tiempo de ejecución. Podemos lograr esto utilizando la reflexión de la siguiente manera:

Persona persona = new Persona();
Type tipoPersona = persona.GetType();

PropertyInfo propiedadNombre = tipoPersona.GetProperty("Nombre");
propiedadNombre.SetValue(persona, "Luis");

Este sería la clase persona que usamos en el ejemplo

public class Persona
{
    public string Nombre { get; set; }
}

En este ejemplo,

  • Estamos obteniendo la propiedad Nombre de la clase Persona utilizando la reflexión
  • Luego, utilizamos el método SetValue() para cambiar su valor a “Luis”.

Creación de instancias dinámicas

La reflexión permite crear instancias de tipos dinámicos en tiempo de ejecución usando Activator.CreateInstance.

Type tipoPersona = typeof(Persona);
object instancia = Activator.CreateInstance(tipoPersona);

PropertyInfo propiedadNombre = tipoPersona.GetProperty("Nombre");
propiedadNombre.SetValue(instancia, "Luis");

Console.WriteLine("Nombre: " + propiedadNombre.GetValue(instancia));

Aquí,

  • Creamos una instancia de Persona en tiempo de ejecución
  • Establecemos su propiedad Nombre mediante reflexión.

Invocación de métodos

Para invocar métodos dinámicamente, utilizamos MethodInfo.

Type tipoCalculadora = typeof(Calculadora);
object instancia = Activator.CreateInstance(tipoCalculadora);

MethodInfo metodoSumar = tipoCalculadora.GetMethod("Sumar");
object resultado = metodoSumar.Invoke(instancia, new object[] { 3, 5 });

Console.WriteLine("Resultado de Sumar: " + resultado);
public class Calculadora
{
    public int Sumar(int a, int b)
    {
        return a + b;
    }
}

En este ejemplo, se invoca el método Sumar de la clase Calculadora de manera dinámica y se imprime el resultado.

Cargar ensamblados en tiempo de ejecución

La reflexión permite cargar ensamblados en tiempo de ejecución y explorar sus tipos y miembros.

Assembly ensamblado = Assembly.Load("mscorlib");
Type[] tipos = ensamblado.GetTypes();

foreach (var tipo in tipos)
{
	Console.WriteLine("Tipo: " + tipo.Name);
}

Este ejemplo carga el ensamblado mscorlib y lista todos los tipos que contiene.

Uso de atributos personalizados

La reflexión permite trabajar con atributos personalizados, pudiendo leer o añadir metadatos a los elementos en tiempo de ejecución.

Type tipo = typeof(MiClase);

if (tipo.GetCustomAttributes(typeof(MiAtributo), false).FirstOrDefault() is MiAtributo atributoClase)
{
	Console.WriteLine("Descripción de la clase: " + atributoClase.Descripcion);
}

MethodInfo metodo = tipo.GetMethod("MetodoEjemplo");
if (metodo.GetCustomAttributes(typeof(MiAtributo), false).FirstOrDefault() is MiAtributo atributoMetodo)
{
	Console.WriteLine("Descripción del método: " + atributoMetodo.Descripcion);
}
[Info("Esta es una clase de ejemplo.")]
public class MiClase
{
    [Info("Este es un método de ejemplo.")]
    public void MetodoEjemplo() { }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MiAtributo : Attribute
{
    public string Descripcion { get; }

    public MiAtributo(string descripcion)
    {
        Descripcion = descripcion;
    }
}

En este ejemplo,

  • Se definen y utilizan atributos personalizados para proporcionar descripciones a una clase y a un método
  • Se accede a esos atributos en tiempo de ejecución usando reflexión

La reflexión es una característica avanzada y muy útil, y que nos permite hacer cosas que de otra forma no serían posibles. Pero, en general la reflexión es lenta, y compleja.


Por lo tanto, debe utilizarse con precaución y solo cuando sea realmente necesario.