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 clasePersona
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.