Las clases
de un ensamblado usando Reflection Por ejemplo, para saber los formularios de nuestra aplicación |
Publicado el 13/Sep/2004 Nota del martes 18 de diciembre de 2018: |
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 SubComo 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 2004Aquí tienes el código completo tanto para VB como para C#: reflectionTiposdeunensamblado.zip 21 KB
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
//----------------------------------------------------------------------------- // 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); } } }