Comprobación del tipo de un objeto y moldeado (casting)
Comprobación del tipo de un objeto y moldeado (casting)
===
Comprobación del tipo de un objeto y moldeado (casting)
En algunas circunstancias, puede ser útil codificar un procedimiento que reciba como parámetro un objeto genérico, y dependiendo del tipo del objeto, ejecutar ciertos métodos.
Si tipificamos un parámetro como un objeto genérico, es decir, de tipo Object, necesitamos implementar dentro del procedimiento un mecanismo que, en primer lugar, compruebe a qué tipo pertenece el objeto, y por otra parte, acceda adecuadamente a su lista de miembros.
Como ejemplo consideremos el siguiente caso: creamos una clase Empleado y otra Factura, las cuales vemos en el Código fuente 265.
Public Class Empleado Public piID As String Public psNombre As String
Public Overridable Sub VerDatos() Console.WriteLine("Código de empleado: {0}, nombre: {1}", _ Me.piID, Me.psNombre) End Sub End Class
Public Class Factura Public pdtFecha As Date Public piImporte As Integer
Public Sub Emitir()
Programación con Visual Basic .NET © Grupo EIDOS
Console.WriteLine("Se procede a generar la siguiente factura:") Console.WriteLine("Fecha de factura: {0}", Me.pdtFecha) Console.WriteLine("Importe de la factura: {0}", Me.piImporte)
End Sub End Class
Código fuente 265
A continuación, necesitamos un procedimiento al que podamos pasar indistintamente un objeto Empleado o Factura. Dentro de dicho procedimiento, al que denominaremos ManipularVarios( ), y que llamaremos desde Main( ), comprobaremos el tipo de objeto llamando al método GetType( ), que implementan todos los objetos del entorno, debido a que es un método de la clase Object, y como ya sabemos, todas las clases heredan implícitamente de Object.
GetType( ) devuelve un objeto de la clase Type. Esta clase es de una enorme utilidad, ya que nos proporciona toda la información relativa al tipo del objeto. Para nuestro problema particular, interrogaremos a la propiedad Name del objeto, que nos devolverá una cadena con el nombre de la clase a que pertenece el objeto.
Conociendo ya el tipo de objeto con el que tratamos, utilizaremos la función CType( ), que realiza un moldeado de la variable que contiene el objeto hacia un tipo determinado, y nos permite acceder a los elementos del objeto.
El lector puede argumentar que no sería necesario el uso de CType(), y en efecto es así; podríamos haber utilizado directamente la variable, situando a continuación el método a ejecutar. Esta técnica, no obstante, tiene el inconveniente de que utiliza enlace tardío para acceder al objeto.
CType( ) sin embargo, tiene la ventaja de que opera bajo enlace temprano, con lo cuál, el rendimiento en ejecución es mayor. Veamos el código cliente que accedería a estas clases en el Código fuente 266.
Module Module1 Public Sub Main() Dim loEmple As New Empleado() loEmple.piID = 58 loEmple.psNombre = "Elena Peral" ManipularVarios(loEmple)
Dim loFac As New Factura() loFac.pdtFecha = "25/2/2002" loFac.piImporte = 475 ManipularVarios(loFac)
Console.Read() End Sub
Public Sub ManipularVarios(ByVal loUnObjeto As Object) ' obtenemos información sobre el tipo del objeto Dim loTipoObj As Type loTipoObj = loUnObjeto.GetType()
' comprobamos qué tipo de objeto es, ' y en función de eso, ejecutamos el ' método adecuado Select Case loTipoObj.Name
Case "Empleado" CType(loUnObjeto, Empleado).VerDatos() Case "Factura" CType(loUnObjeto, Factura).Emitir()
© Grupo EIDOS 20. Elementos compartidos e interfaces
End Select End Sub End Module
Código fuente 266
En el caso de clases heredadas, y con métodos sobrescritos, CType( ) discierne la implementación de clase a la que debe dirigirse.
Si añadimos la clase Administrativo como hija de Empleado, y sobrescribimos el método VerDatos( ), cuando pasemos al procedimiento ManipularVarios( ) un objeto Administrativo, dado que esta clase hereda de Empleado, con un sencillo cambio en la estructura Select...Case que comprueba el tipo de objeto, ejecutaremos la implementación sobrescrita del método VerDatos( ) en la clase Administrativo. Veamos el Código fuente 267.
Module Module1 Public Sub Main() Dim loEmple As New Empleado() loEmple.piID = 58 loEmple.psNombre = "Elena Peral" ManipularVarios(loEmple)
Dim loAdmin As New Administrativo() loAdmin.piID = 80 loAdmin.psNombre = "Alfredo Iglesias" ManipularVarios(loAdmin)
Dim loFac As New Factura() loFac.pdtFecha = "25/2/2002" loFac.piImporte = 475 ManipularVarios(loFac)
Console.Read() End Sub
Public Sub ManipularVarios(ByVal loUnObjeto As Object) ' obtenemos información sobre el tipo del objeto Dim loTipoObj As Type loTipoObj = loUnObjeto.GetType()
' comprobamos qué tipo de objeto es, ' y en función de eso, ejecutamos el ' método adecuado Select Case loTipoObj.Name
Case "Empleado", "Administrativo" CType(loUnObjeto, Empleado).VerDatos() Case "Factura" CType(loUnObjeto, Factura).Emitir() End Select End Sub End Module
Public Class Administrativo Inherits Empleado
Public pdtFHAlta As Date
Public Overrides Sub VerDatos() Console.WriteLine("DATOS DEL ADMINISTRATIVO") Console.WriteLine("==================") Console.WriteLine("Código: {0}", Me.piID)
Programación con Visual Basic .NET © Grupo EIDOS
Console.WriteLine("Nombre: {0}", Me.psNombre) End Sub
Public Function MesAlta() Return Format(Me.pdtFHAlta, "MMMM") End Function End Class
Código fuente 267
Pero ¿cómo podríamos ejecutar el método particular MesAlta( ) de Administrativo, que no se encuentra en Empleado?, pues creando en la estructura Select...Case, un caso particular que compruebe cuándo estamos tratando con un objeto Administrativo. Veámoslo en el Código fuente 268.
Public Sub ManipularVarios(ByVal loUnObjeto As Object) ' obtenemos información sobre el tipo del objeto Dim loTipoObj As Type loTipoObj = loUnObjeto.GetType()
' comprobamos qué tipo de objeto es, ' y en función de eso, ejecutamos el ' método adecuado Select Case loTipoObj.Name
Case "Empleado" CType(loUnObjeto, Empleado).VerDatos()
Case "Administrativo" ' <-- añadimos este caso a la estructura CType(loUnObjeto, Administrativo).VerDatos() Console.WriteLine("El administrativo comenzó en {0}", _
CType(loUnObjeto, Administrativo).MesAlta())
Case "Factura" CType(loUnObjeto, Factura).Emitir() End Select End Sub
Código fuente 268
Miembros compartidos (shared) de una clase
Los miembros compartidos o shared son aquellos que no precisan de una instancia previa de un objeto
de la clase para poder ser utilizados, aunque pueden también ser usados por una instancia de la clase. Dentro de este contexto, podemos pues clasificar los miembros de una clase en dos categorías: • Miembros de instancia (instance members). Son aquellos a los que accedemos a través de
un objeto instanciado previamente de la clase. • Miembros compartidos (shared members). Son aquellos a los que podemos acceder sin
necesidad de que exista un objeto creado de la clase. Podemos declarar como compartidos los métodos, propiedades y campos de una clase. Para ello
deberemos emplear la palabra clave Shared en la declaración.
© Grupo EIDOS 20. Elementos compartidos e interfaces
Para utilizar desde el código cliente un miembro compartido, tan sólo debemos poner el nombre de la clase a la que pertenece, el punto y el nombre del miembro a utilizar.
El ejemplo del Código fuente 269 demuestra como podemos ejecutar un método compartido sin haber instanciado antes un objeto de la clase a la que pertenece dicho método.
Module General Sub Main() Dim lsValor As String ' aunque no hemos instanciado objetos ' de la clase Empleado, podemos llamar ' a este método compartido Console.WriteLine("Nombre del mes: {0}", Empleado.VerNombreMes())
' ahora creamos una instancia de la clase Dim loEmpleado1 As New Empleado() lsValor = loEmpleado1.VerNombreDia() Console.WriteLine("Nombre del día: {0}", lsValor)
Console.ReadLine() End Sub End Module
Public Class Empleado Public Shared Function VerNombreMes() As String ' este método puede ser llamado ' directamente empleando el nombre ' de la clase como calificador Return Format(Now(), "MMMM")
End Function
Public Function VerNombreDia() As String ' este método precisa de una instancia ' para ser llamado Return Format(Now(), "dddd")
End Function End Class
Código fuente 269
En el caso de variables de clase declaradas como miembros compartidos, este tipo de variable sólo es creado una vez, manteniendo su valor para todas las instancias de la clase. Esto contrasta con los miembros de instancia, de los que se crea una copia particular para cada objeto.
El efecto de miembro compartido se hace más patente cuando se aplica sobre variables, por ello, en el ejemplo del Código fuente 270, creamos dos campos compartidos para la clase Empleado; uno de ellos actuará como contador de los objetos creados de la clase, usando el método constructor para ser incrementado. El otro nos servirá para comprobar que siendo compartido no se inicializa, y mantiene el valor asignado previamente.
Module General Sub Main() ' accedemos a la variable compartida ' y le asignamos valor Empleado.psApellidos = "Naranjo"
' instanciamos un primer objeto Empleado Dim loEmp1 As New Empleado()
Programación con Visual Basic .NET © Grupo EIDOS
' asignamos valor a su variable de instancia loEmp1.psNombre = "Luis" ' mostramos las dos variables del objeto Console.WriteLine("Objeto loEmp1 - valores de sus variables") Console.WriteLine("psNombre: {0} - psApellidos: {1}", _
loEmp1.psNombre, loEmp1.psApellidos)
Console.WriteLine()
' instanciamos un segundo objeto Empleado Dim loEmp2 As New Empleado() ' asignamos valor a su variable de instancia loEmp2.psNombre = "Juan" ' mostramos las dos variables del objeto Console.WriteLine("Objeto loEmp2 - valores de sus variables") Console.WriteLine("psNombre: {0} - psApellidos: {1}", _
loEmp2.psNombre, loEmp2.psApellidos)
Console.WriteLine() ' ahora mostramos el valor de ' la variable compartida miContar Console.WriteLine("Se han instanciado {0} objetos de la clase Empleado", _
Empleado.piContar) Console.ReadLine() End Sub End Module
Public Class Empleado Public psNombre As String ' miembro de instancia Public Shared psApellidos As String ' miembro compartido Public Shared piContar As Integer ' miembro compartido
Public Sub New() ' por cada instancia de la clase creada, ' incrementar este campo compartido Me.piContar += 1
End Sub End Class
Código fuente 270