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