Requerimientos
- Se desea ver los tipos que hay dentro de un ensamblado .NET: interfaces, enumeraciones, clases, etc.
- Se desea listar las propiedades de un tipo.
- Se desea listar los métodos de un tipo.
- Se desea ver los parámetros de un método.
- Se desea abrir dinámicamente un formulario pasando como parámetro su nombre.
- Se desea ejecutar cualquier método pasando los valores de sus parámetros, incluyendo métodos sobrecargados (varios con el mismo nombre pero con diferentes parámetros).
Solución
Usar Reflection para obtener información del Ensamblado, sus Tipos y miembros, también para crear instancias de objetos en forma dinámica y ejecutar cualquier método.
A continuación les detallo el código de un Utilitario que he creado hace varios años pero que lo he mejorado para probar los métodos sobre cargados y para pasar parámetros.
Crear la Aplicación Windows Forms en C#
Crear un proyecto Windows Forms en C# llamado: "NetObjectViewer" y agregar una clase llamada "Rutinas" para poder probar sus métodos mediante el visor.
using System;
using System.Windows.Forms;
namespace NetObjectViewer
{
public class Rutinas
{
public void mostrarMensaje()
{
MessageBox.Show("Hola que tal");
}
public void mostrarMensaje(string nombre)
{
MessageBox.Show("Hola que tal " + nombre);
}
public void mostrarMensaje(string nombre,string apellido)
{
MessageBox.Show("Hola que tal " + nombre + " " + apellido);
}
public int sumaEnteros(int x, int y)
{
return (x+y);
}
public int sumaEnteros(int x, int y,int z)
{
return (x + y +z);
}
public decimal sumaDecimal(decimal x, decimal y)
{
return (x + y);
}
public decimal sumaDecimal(decimal x, decimal y, decimal z)
{
return (x + y + z);
}
}
}
Cambiar el nombre del formulario a "frmVisor" y realizar el diseño tal como se muestra a continuación.
Escribir el siguiente código en el formulario:
using System;
using System.Text; //StringBuilder
using System.Drawing;
using System.Collections.Generic; //List
using System.Reflection; //Assembly, Type, PropertyInfo, MethodInfo
using System.Windows.Forms;
namespace NetObjectViewer
{
public partial class frmVisor : Form
{
private Assembly oEnsamblado;
private Type[] oTipos;
public frmVisor()
{
InitializeComponent();
}
private void configurarGrilla(object sender, EventArgs e)
{
DataGridViewTextBoxColumn col1=new DataGridViewTextBoxColumn();
col1.Width=100;
col1.ReadOnly = true;
dgvParametro.Columns.Add(col1);
DataGridViewTextBoxColumn col2 = new DataGridViewTextBoxColumn();
col2.Width = 80;
dgvParametro.Columns.Add(col2);
}
private void abrirEnsamblado_ListarTipos(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "Selecciona Archivo exe o dll de .NET";
ofd.Filter = "Archivos ensamblados .NET|*.exe;*.dll";
if (ofd.ShowDialog().Equals(DialogResult.OK))
{
txtEnsamblado.Text = ofd.FileName;
try
{
oEnsamblado = Assembly.LoadFrom(ofd.FileName);
//Limpiar las listas
dgvParametro.Rows.Clear();
lstMetodo.Items.Clear();
lstPropiedad.Items.Clear();
lstTipo.Items.Clear();
//Listar los tipos
oTipos = oEnsamblado.GetTypes();
lstTipo.BeginUpdate();
foreach (Type oTipo in oTipos)
{
lstTipo.Items.Add(oTipo.Name);
}
lstTipo.EndUpdate();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
private void listarMiembros(object sender, EventArgs e)
{
Type oTipo = oTipos[lstTipo.SelectedIndex];
//Limpiar las listas
dgvParametro.Rows.Clear();
lstMetodo.Items.Clear();
lstPropiedad.Items.Clear();
//Listar las propiedades
PropertyInfo[] oPropiedades = oTipo.GetProperties();
lstPropiedad.BeginUpdate();
foreach (PropertyInfo oPropiedad in oPropiedades)
{
lstPropiedad.Items.Add(oPropiedad.Name);
}
lstPropiedad.EndUpdate();
//Listar los metodos
MethodInfo[] oMetodos = oTipo.GetMethods();
lstMetodo.BeginUpdate();
StringBuilder sb=new StringBuilder();
ParameterInfo[] oParametros;
foreach (MethodInfo oMetodo in oMetodos)
{
sb.Clear();
sb.Append(oMetodo.Name);
sb.Append("(");
oParametros = oMetodo.GetParameters();
for (int i = 0; i < oParametros.Length; i++)
{
sb.Append(oParametros[i].ParameterType.Name);
if (i < oParametros.Length-1) sb.Append(",");
}
sb.Append(")");
lstMetodo.Items.Add(sb.ToString());
}
lstMetodo.EndUpdate();
}
private MethodInfo obtenerMetodo()
{
MethodInfo oMetodo = null;
string firmaMetodo = lstMetodo.Text;
int posInicio = firmaMetodo.IndexOf("(");
int posFin = firmaMetodo.Length - 1;
string nombreMetodo = firmaMetodo.Substring(0, posInicio);
string tipos = firmaMetodo.Substring(posInicio + 1, posFin - posInicio - 1);
Type[] oPars = null;
if (tipos.Length.Equals(0)) oPars = new Type[0] { };
else
{
string[] entradas = tipos.Split(',');
List<Type> listaPars = new List<Type>();
for (int i = 0; i < entradas.Length; i++)
{
listaPars.Add(Type.GetType("System." + entradas[i]));
}
oPars = listaPars.ToArray();
}
Type oTipo = oTipos[lstTipo.SelectedIndex];
oMetodo= oTipo.GetMethod(nombreMetodo, oPars);
return (oMetodo);
}
private void listarParametros(object sender, EventArgs e)
{
if (lstMetodo.SelectedIndex > -1)
{
MethodInfo oMetodo = obtenerMetodo();
if (oMetodo != null)
{
ParameterInfo[] oParametros = oMetodo.GetParameters();
dgvParametro.Rows.Clear();
dgvParametro.RowCount = oParametros.Length;
for (int i = 0; i < oParametros.Length; i++)
{
dgvParametro.Rows[i].Cells[0].Value = oParametros[i].Name;
}
}
}
else MessageBox.Show("Selecciona el método a ver sus parámetros");
}
private void abrirFormulario(object sender, EventArgs e)
{
if (lstTipo.SelectedIndex > -1)
{
Type oTipo = oTipos[lstTipo.SelectedIndex];
if (oTipo.BaseType.Name.Equals("Form"))
{
object obj = Activator.CreateInstance(oTipo);
if (obj != null)
{
Form frm = (Form)obj;
frm.ShowDialog();
}
}
else MessageBox.Show("El tipo seleccionado No es un formulario");
}
else MessageBox.Show("Selecciona el formulario a abrir");
}
private void ejecutarMetodo(object sender, EventArgs e)
{
if (lstMetodo.SelectedIndex > -1)
{
MethodInfo oMetodo = obtenerMetodo();
if (oMetodo != null)
{
object[] pars = null;
ParameterInfo[] oParametros = oMetodo.GetParameters();
if (oParametros.Length > 0)
{
List<object> listaPars = new List<object>();
Type tipo=null;
object valor=null;
for (int i = 0; i < oParametros.Length; i++)
{
tipo = oParametros[i].ParameterType;
valor=dgvParametro.Rows[i].Cells[1].Value;
if (valor != null) listaPars.Add(Convert.ChangeType(valor, tipo));
else listaPars.Add(valor);
}
pars = listaPars.ToArray();
}
Type oTipo = oTipos[lstTipo.SelectedIndex];
object obj = Activator.CreateInstance(oTipo);
if (obj != null)
{
object rpta = oMetodo.Invoke(obj, pars);
if (rpta != null) MessageBox.Show(rpta.ToString());
}
}
}
else MessageBox.Show("Selecciona el método a ejecutar");
}
}
}
Nota: El método controlador: "configurarGrilla"esta asociado al evento "Load" del formulario.
Ejecutar y Probar el Formulario Creado
Grabar la aplicación, compilar y ejecutar. En el evento load se crean las 2 columnas de la grilla de parámetros del método seleccionado.
Seleccionar un ensamblado, por ejemplo el propio ejecutable: "NetObjectViewer.exe" y luego seleccionar un tipo, por ejemplo el propio formulario.
Estando seleccionado el formulario "frmVisor" clic al botón "Abrir Formulario" y este se mostrará como un diálogo.
Seleccionar la clase "Rutinas" y luego seleccionar cualquier método, observar que aparece la grilla mostrando sus parámetros. Probar primero las funciones que no retornan valores y luego las que retornan valores, dando clic al botón "Ejecutar Método".
Si no se llenan los valores de los parámetros, por defecto es nulo, pero igual devuelve un resultado, ahora probar pasar parámetros escribiendo directamente sobre la segunda columna de la grilla y dando Enter para configurar el valor del parámetro. Luego clic al botón "Ejecutar Método" y ver el resultado con parámetros.
Nota: Si se selecciona un método que tiene parámetros de tipo objeto, la aplicación se caerá, falta controlar los parámetros de tipo objetos.
Comentario Final
En esta demostración hemos visto "Reflection" en acción para inspeccionar cualquier ensamblado (exe o dll) creado en .NET, listando algunos miembros como propiedades y métodos, también hemos creado dinámicamente objetos y ejecutado métodos, inclusive sobrecargados. Espero les guste y comenten.
Descarga
Demo11_NetObjectViewer