Índice de la sección dedicada a .NET (en el Guille) Cómo... en .NET

Las clases de un ensamblado usando Reflection
Por ejemplo, para saber los formularios de nuestra aplicación

Código para Visual Basic.NET (VB.NET)

Código para C Sharp (C#)


Publicado el 13/Sep/2004
Actualizado el 13/Sep/2004
Autor: Guillermo 'guille' Som

 

Introducción:

En este código veremos cómo saber las clases que contiene un ensamblado, esto puede ser útil para cargar dinámicamente (en tiempo de ejecución) alguna de las clases que contiene, es decir, crear nuevos objetos de alguna de esas clases, como por ejemplo un formulario, ya que al fin y al cabo, un formulario no es ni más ni menos que una clase.

Cuando trabajamos con VB6, podemos acceder a una colección que nos indica los formularios en ejecución, es decir, los formularios que nuestra aplicación ha cargado en la memoria (instanciado), lo que no es posible (con VB6) es averiguar los formularios (y clases) que nuestra aplicación contiene.
Realmente en VB6 puede que no tenga mucho sentido, salvo algunas ocasiones, saber cuales son los formularios que la aplicación contiene... pero en .NET si que puede tener más interés ya que podemos crear nuestros propios ensamblados de forma dinámica, es decir, "al vuelo", todo ello mediante código, aunque este tema no lo tocaremos en esta ocasión, pero si te interesa investigar sobre ello, puedes echarle un vistazo al espacio de nombres System.Reflection.Emit y System.CodeDOM de los que te pongo los links de la ayuda de MSDN así como al acceso a esa información (en español) desde la Web:

ms-help://.../cpref/html/frlrfSystemReflectionEmit.htm
(http://msdn.microsoft.com/library/spa/cpref/html/frlrfSystemReflectionEmit.asp)
ms-help://.../cpref/html/frlrfSystemCodeDom.htm
(http://msdn.microsoft.com/library/spa/cpref/html/frlrfSystemCodeDom.asp)

 

Las clases del ensamblado actual (el que está en ejecución):

Para este ejemplo, veremos cómo cargar (mostrar) las clases que contiene el ensamblado actual, es decir el que se ha cargado con nuestra aplicación.
Para ello usaremos el método estático GetExecutingAssembly() de la clase Assembly, el cual nos dará la posibilidad de acceder al ensamblado que se está ejecutando, es decir el de nuestra aplicación.
De ese ensamblado podremos acceder a las clases (tipos) que contiene usando el método GetTypes().

Antes de pasar a mostrar el código que muestre dicha información, necesitaremos un formulario (también podríamos hacerlo mediante la consola, pero...), en el cual tendremos como mínimo un ListBox en el que se mostrarán las clases y un botón para actualizar dicha información.
Al ListBox le daremos el nombre lstForms y el botón tendrá el nombre btnRefescar, también tendremos una etiqueta llamada LabelInfo en la que mostraremos alguna otra cosa...
En el evento Load del formulario principal asignaremos a una variable de tipo Assembly (que estará definida a nivel de módulo y se llamará ass) el valor devuelto por el método GetExecutingAssembly, para poder usarlo en el resto de métodos de nuestra aplicación.

 

Nota:
Más abajo te muestro el código completo, tanto para VB como para C#, en el cual existirá otro botón "Mostrar" (btnMostrar) con el que cargaremos en memoria el formulario que hayamos seleccionado del ListBox.

 

Empecemos viendo el código de carga del formulario (evento Form_Load), en el que asignaremos el valor de la variable ass y llamaremos al método mostrarForms que será el que se encargue de rellenar el ListBox:

ass = System.Reflection.Assembly.GetExecutingAssembly()
'
mostrarForms()

El método mostrarForms recorre todos los tipos del ensamblado que está en ejecución y muestra el nombre completo (incluido el espacio de nombres) de cada una de las clases, así como el tipo subyacente, (que bien queda el palabro este) que al fin y al cabo quiere decir que nos muestra el tipo en el que está basado el tipo que estamos mostrando:

Private Sub mostrarForms()
    ' llena el listbox con los formularios de esta aplicación
    ' estén o no en memoria
    '
    For Each t As Type In ass.GetTypes()
        lstForms.Items.Add(t.FullName)
        lstForms.Items.Add("  --> " & t.BaseType.Name)
    Next
End Sub

Como podemos comprobar, primero se añade el nombre completo del tipo (clase) y posteriormente el tipo base de dicho tipo, en el caso de los formularios, el tipo base será Form (salvo que nuestro formulario esté basado en otro formulario) y en el caso de las clases, el tipo base será Object (salvo que la clase esté basada en otra clase).

 

Nota:
Para que este código muestre algo más que el formulario normal, te recomendaría que crearas algunos formularios extras así como algunas otras clases, (algunas normales y otras basadas en otras clases), de esta forma podrás comprobar lo que se muestra en cada caso.

 

El botón Refrescar simplemente se encargará de limpiar el contenido del listbox y llamar al método mostrarForms:

Private Sub btnRefrescar_Click(ByVal sender As System.Object, _
                               ByVal e As System.EventArgs) _
                               Handles btnRefrescar.Click
    lstForms.Items.Clear()
    mostrarForms()
End Sub

 

Crear una instancia de una clase sabiendo el nombre:

Ahora veremos cómo crear una instancia de una de las clases de nuestro ensamblado, y en caso de que sea un formulario, haremos que se muestre. El código estará en el evento Click del botón btnMostrar.
Lo que haremos es "intentar" crear una nueva instancia del elemento seleccionado en el listBox, y en caso de que sea un formulario, llamaremos al método Show() para que se muestre; también comprobaremos si es el formulario actual, en caso de que así sea, lo cerraremos para no tener dos copias en memoria.

Aquí, el punto interesante es el método CreateInstance() de la clase System.Activator, el cual, entre otras cosas, nos permite crear una nueva instancia a partir de un tipo, el valor devuelto por ese método es de tipo Object, por tanto, si queremos usar algunos de los métodos de dicha clase tendremos que convertirlo en el tipo adecuado.
Pero como dice el título de la sección, la instancia la crearemos a partir del nombre del objeto y la sobrecarga que vamos a usar de CreateInstance recibe un "tipo" como parámetro, por tanto necesitaremos crear un tipo a partir de una cadena, eso lo haremos usando una sobre carga del método GetType de la clase Assembly, al que le indicaremos el nombre del tipo que queremos "manipular", veamos el código para que lo entiendas mejor:

Private Sub btnMostrar_Click(ByVal sender As System.Object, _
                             ByVal e As System.EventArgs) _
                             Handles btnMostrar.Click
    ' Mostrar el formulario seleccionado
    If lstForms.SelectedItems.Count > 0 Then
        ' el nombre de la clase que queremos instanciar
        Dim s As String = lstForms.SelectedItem.ToString
        LabelInfo.Text = s
        ' creamos un tipo a partir del nombre
        Dim t As Type = ass.GetType(s)
        ' instanciamos un nuevo objeto en la memoria
        Dim o As Object
        ' por si hemos seleccionado algo que no es una clase
        Try
            o = Activator.CreateInstance(t)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Mostrar formularios")
            Exit Sub
        End Try
        '
        ' si no es un formulario, mostramos un aviso y salimos
        If Not (TypeOf o Is Form) Then
            MessageBox.Show(s & ", no es un formulario", "Mostrar formularios")
            Exit Sub
        End If
        '
        ' convertimos el objeto en un formulario
        ' como sabemos que si llega aquí es un formulario,
        ' usamos DirectCast que hace menos trabajo que CType.
        Dim f As Form = DirectCast(o, Form)
        ' si el nombre es el de este formulario,
        ' lo cerramos y salimos.
        If f.Name = Me.Name Then
            ' no volver a crear este formulario
            f.Close()
            Me.BringToFront()
            ' Return también se puede usar para salir de un Sub de VB
            Return
        End If
        ' lo mostramos.
        f.Show()
    End If
End Sub

 

Bueno, y esto es todo por ahora, espero que sigas investigando por tu cuenta y saques algún provecho de las cosillas que la reflexión (reflection) nos permite en .NET Framework.

Nos vemos.
Guillermo
Nerja, 13 de septiembre de 2004

Aquí tienes el código completo tanto para VB como para C#: reflectionTiposdeunensamblado.zip 21 KB

 


Código para Visual Basic.NET (VB.NET)El código para VB .NET
Se incluye el código de dos clases para añadir al proyecto.

 

'------------------------------------------------------------------------------
' Mostrar los formularios en ejecución y poder manipularlos         (27/May/04)
'
' Realmente muestra las clases que incluye el ensamblado
'
' ©Guillermo 'guille' Som, 2004
'------------------------------------------------------------------------------
Imports System
Imports System.Windows.Forms

Public Class Form1
    Inherits System.Windows.Forms.Form
    '
#Region " Código generado por el Diseñador de Windows Forms "
    '...
#End Region
    '
    Private ass As System.Reflection.Assembly
    '
    Private Sub btnRefrescar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRefrescar.Click
        lstForms.Items.Clear()
        '
        mostrarForms()
        LabelInfo.Text = LabelInfo.Tag.ToString()
    End Sub
    '
    Private Sub btnMostrar_Click(ByVal sender As System.Object, _
                                 ByVal e As System.EventArgs) _
                                 Handles btnMostrar.Click
        ' Mostrar el formulario seleccionado
        If lstForms.SelectedItems.Count > 0 Then
            ' el nombre de la clase que queremos instanciar
            Dim s As String = lstForms.SelectedItem.ToString
            LabelInfo.Text = s
            ' creamos un tipo a partir del nombre
            Dim t As Type = ass.GetType(s)
            ' instanciamos un nuevo objeto en la memoria
            Dim o As Object
            ' por si hemos seleccionado algo que no es una clase
            Try
                o = Activator.CreateInstance(t)
            Catch ex As Exception
                MessageBox.Show(ex.Message, "Mostrar formularios")
                Exit Sub
            End Try
            '
            ' si no es un formulario, mostramos un aviso y salimos
            If Not (TypeOf o Is Form) Then
                MessageBox.Show(s & ", no es un formulario", "Mostrar formularios")
                Exit Sub
            End If
            '
            ' convertimos el objeto en un formulario
            ' como sabemos que si llega aquí es un formulario,
            ' usamos DirectCast que hace menos trabajo que CType.
            Dim f As Form = DirectCast(o, Form)
            ' si el nombre es el de este formulario,
            ' lo cerramos y salimos.
            If f.Name = Me.Name Then
                ' no volver a crear este formulario
                f.Close()
                Me.BringToFront()
                ' Return también se puede usar para salir de un Sub de VB
                Return
            End If
            ' mostramos el formulario.
            f.Show()
        Else
            LabelInfo.Text = LabelInfo.Tag.ToString()
        End If
    End Sub
    '
    Private Sub btnCerrar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCerrar.Click
        Me.Close()
    End Sub
    '
    Private Sub mostrarForms()
        ' llena el listbox con los formularios de esta aplicación
        ' estén o no en memoria
        '
        For Each t As Type In ass.GetTypes()
            lstForms.Items.Add(t.FullName)
            lstForms.Items.Add("  --> " & t.BaseType.Name)
        Next
    End Sub
    '
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        LabelInfo.Tag = "©Guillermo 'guille' Som, 2004"
        LabelInfo.Text = LabelInfo.Tag.ToString
        '
        ass = System.Reflection.Assembly.GetExecutingAssembly()
        '
        mostrarForms()
        '
    End Sub
End Class

 

Las clases de pruebas:

Public Class Prueba
    Public ID As String
    Public Nombre As String
    '
    Public Overrides Function ToString() As String
        Return String.Format("Prueba, ID: {0}, Nombre: {1}", ID, Nombre)
    End Function
End Class

Public Class Prueba2
    Inherits Prueba
    '
    Public Overrides Function ToString() As String
        Return String.Format("Prueba2, ID: {0}, Nombre: {1}", ID, Nombre)
    End Function
End Class

 


Código para C Sharp (C#)El código para C#

 

//-----------------------------------------------------------------------------
// Mostrar los formularios en ejecución y poder manipularlos        (27/May/04)
//
// Realmente muestra las clases que incluye el ensamblado
//
// Convertido de VB a C#                                            (13/Sep/04)
//
// ©Guillermo 'guille' Som, 2004
//-----------------------------------------------------------------------------
using System;
using System.Windows.Forms;

namespace formualriosActivosCS
{
    public class Form1 : System.Windows.Forms.Form
    {
          //...
        /// <summary>
        /// Punto de entrada principal de la aplicación.
        /// </summary>
        [STAThread]
        static void Main() 
        {
            Application.Run(new Form1());
        }
        //
        private System.Reflection.Assembly ass;
        //
        private void btnRefrescar_Click(System.Object sender, System.EventArgs e) 
        {
            lstForms.Items.Clear();
            //
            mostrarForms();
            LabelInfo.Text = LabelInfo.Tag.ToString();
        }  
        //
        private void btnMostrar_Click(System.Object sender, System.EventArgs e) 
        {
            // Mostrar el formulario seleccionado
            if( lstForms.SelectedItems.Count > 0 )
            {
                // el nombre de la clase que queremos instanciar
                string s = lstForms.SelectedItem.ToString();
                LabelInfo.Text = s;
                // creamos un tipo a partir del nombre
                Type t = ass.GetType(s);
                // instanciamos un nuevo objeto en la memoria
                object o;
                // por si hemos seleccionado algo que no es una clase
                try
                {
                    o = Activator.CreateInstance(t);
                }
                catch(Exception ex)
                {
                    MessageBox.Show(ex.Message, "Mostrar formularios");
                    return;
                }
                //
                // si no es un formulario, mostramos un aviso y salimos
                if(  !(o is Form) )
                {
                    MessageBox.Show(s + ", no es un formulario", "Mostrar formularios");
                    return;
                }
                //
                // convertimos el objeto en un formulario
                // como sabemos que si llega aquí es un formulario,
                // usamos DirectCast que hace menos trabajo que CType.
                Form f = (Form)o;
                // si el nombre es el de este formulario,
                // lo cerramos y salimos.
                if( f.Name == this.Name )
                {
                    // no volver a crear este formulario
                    f.Close();
                    this.BringToFront();
                    return;
                }
                // mostramos el formulario.
                f.Show();
            }
            else
            {
                LabelInfo.Text = LabelInfo.Tag.ToString();
            }
        }  
        //
        private void btnCerrar_Click(System.Object sender, System.EventArgs e) 
        {
            this.Close();
        }  
        //
        private void mostrarForms() 
        {
            // llena el listbox con los formularios de esta aplicación
            // estén o no en memoria
            foreach(Type t in ass.GetTypes())
            {
                lstForms.Items.Add(t.FullName);
                lstForms.Items.Add("  --> " + t.BaseType.Name);
            }
        }
        //
        private void Form1_Load(System.Object sender, System.EventArgs e) 
        {
            LabelInfo.Tag = "©Guillermo 'guille' Som, 2004";
            LabelInfo.Text = LabelInfo.Tag.ToString();
            //
            ass = System.Reflection.Assembly.GetExecutingAssembly();
            //
            mostrarForms();
        }    
    }
}

 

Las clases de pruebas:

using System;

namespace formualriosActivosCS
{
    public class Prueba
    {
        public string ID;
        public string Nombre;
        //
        public override string ToString() 
        {
            return String.Format("Prueba, ID: {0}, Nombre: {1}", ID, Nombre);
        }  
    }
 
    public class Prueba2:Prueba
    {
        //
        public override string ToString() 
        {
            return String.Format("Prueba2, ID: {0}, Nombre: {1}", ID, Nombre);
        }  
    }
}

 


la Luna del Guille o... el Guille que está en la Luna... tanto monta...