Buscador

Llamadas a una librería desde un ejecutable - II

Una vez seleccionado el archivo DLL correspondiente a la librería aceptaremos esta ventana, añadiéndose la referencia al proyecto del ejecutable. Esto lo podemos comprobar abriendo en la ventana del Explorador de soluciones el elemento References, que como su nombre indica, muestra las referencias establecidas desde el proyecto a otras librerías o ensamblados externos. Ver la Figura 118.
Figura 118. Referencia establecida desde un proyecto hacia la librería.
Por último, y dado que el código de la librería está contenido en su espacio de nombres raíz, que engloba a todo el código de la misma, debemos importar al comienzo del archivo de código del ejecutable, este espacio de nombres, como se muestra en el Código fuente 250.

' importar espacio de nombres raíz de la librería
Imports Rutinas
Module Module1
Sub Main()
' instanciar y trabajar con un objeto de la librería
Dim oContab As Contabilidad
oContab = New Contabilidad()
oContab.NombreBalance = "Saldos"
oContab.MostrarBalance()
Console.Read()
End Sub
End Module
Código fuente 250

Cuando ejecutemos el programa depurando línea a línea, al llegar al código de la librería, se abrirá una ventana en el IDE que nos mostrará la depuración de dicho código. No obstante, cualquier modificación que tengamos que hacer sobre los fuentes de la librería, se recomienda realizarla abriendo el proyecto correspondiente a dicha librería.

Llamadas a una librería desde un ejecutable - I

Finalizada la creación de la librería, pasaremos seguidamente a la fase de creación del ejecutable que hará uso de la misma. En primer lugar crearemos desde VS.NET un nuevo proyecto de tipo consola al que llamaremos Gestión (hacer clic aquí para acceder a este ejemplo). Como ya sabemos, el IDE añade por defecto un archivo al proyecto con el código básico para el mismo: un módulo y un procedimiento Main( ). Ahora debemos proporcionar a este proyecto el enlace o acceso a la librería que creamos anteriormente, para que pueda hacer uso del código que escribimos en ella. 
Este proceso se denomina agregar referencia. En el apartado dedicado a los espacios de nombres explicamos que cada vez que creamos un proyecto, el IDE crea un espacio de nombres que engloba a dicho proyecto o ensamblado; esto es aplicable a cualquier tipo de proyecto: consola, librería, etc. Por lo tanto, en el caso actual, lo que necesitamos es acceder al espacio de nombres de la biblioteca de clases que hemos creado, a través de una referencia establecida desde el ejecutable hacia la librería. Para crear una referencia usaremos la opción de menú Proyecto + Agregar referencia, que nos mostrará una ventana de diálogo, en la que pulsaremos el botón examinar y navegaremos hasta la ruta del disco duro en donde se encuentra la DLL que hemos creado anteriormente. Ver la Figura 117.
Figura 117. Ventana para agregar una referencia a una librería.

Creación de librerías de clases desde VB.NET - II

Ya que este ejemplo trata de ilustrar el proceso de creación de una librería, sin entrar en los detalles de codificación de clases, el código de la clase Contabilidad será muy simple, constando únicamente de una propiedad y un método que posteriormente utilizaremos desde un proyecto de consola o ejecutable. El Código fuente 249 muestra el contenido de la clase Contabilidad.

Public Class Contabilidad
Private mNombreBalance As String
Public Property NombreBalance() As String
Get
Return mNombreBalance
End Get
Set(ByVal Value As String)
mNombreBalance = Value
End Set
End Property
Public Sub MostrarBalance()
Console.WriteLine("Balance actual: {0}", mNombreBalance)
End Sub
End Class
Código fuente 249
Una vez completada la fase de escritura del código de la librería, procederemos a su compilación para generar el correspondiente archivo DLL. Esto lo conseguiremos mediante la opción de menú del IDE Generar + Generar, con lo que obtendremos en el directorio \BIN del proyecto la librería física, en este ejemplo será el archivo Rutinas.DLL.
En el caso de que hayamos generado ya el archivo .DLL, y efectuemos cambios sobre el mismo, las sucesivas generaciones sólo compilarán aquellas partes nuevas o modificadas de la librería. Si necesitamos crear la librería al completo, seleccionaremos la opción de menú Generar + Volver a generar todo.

Creación de librerías de clases desde VB.NET - I

Para crear una librería de enlace dinámico utilizando VB.NET como lenguaje, debemos crear en primer lugar desde el IDE de VS.NET un proyecto de tipo Biblioteca de clases, como vemos en la Figura 116. El proceso es muy similar al empleado para crear un proyecto de tipo consola. En este caso hemos dado el nombre Rutinas al proyecto (hacer clic aquí para acceder a este ejemplo), cuyo contenido inicial será una clase con el nombre Class1, situada en el archivo de código Class1.VB. Podemos cambiar de nombre la clase, o eliminar el archivo del proyecto, añadiendo una clase (y archivo de código) nueva con un nombre diferente. En este caso y para simplificar, cambiaremos el nombre de la clase por Contabilidad (suponiendo que vamos a desarrollar una librería para los procesos de contabilidad de la empresa), dejando Class1.VB como nombre de archivo.

El ensamblado como pieza de construcción de aplicaciones

En terminología .NET, los ejecutables y librerías reciben también la denominación de ensamblado. Los ensamblados, dentro de la arquitectura de desarrollo de la plataforma .NET Framework, constituyen los llamados bloques de construcción de aplicaciones, ya que a semejanza del proceso de construcción de objetos en la vida real, nos permiten, al igual que las piezas de una maquinaria, su interconexión para crear un elemento de mayor entidad, que funcione articulado a partir de un conjunto de piezas especializadas en resolver tareas muy concretas. Un esquema de este funcionamiento se muestra en la Figura 115.
Figura 115. Esquema de la conexión de ensamblados y su funcionamiento conjunto en .NET.
Debido a este hecho, a partir de ahora también nos referiremos a los ejecutables, y sobre todo a las librerías, con el término ensamblado.

Librerías de clases

El formato inicial de librería de enlace dinámico estaba diseñado para contener procedimientos y funciones de forma separada. Sin embargo, la progresiva incorporación de las técnicas OOP a las herramientas de programación, han provocado una evolución en la manera de organizar el código de las librerías de enlace dinámico, pasando actualmente a estar compuestas por clases. 
Debido a este cambio, un ejecutable que requiera hacer uso de una DLL, debe primeramente instanciar un objeto de una de sus clases, llamando después a sus métodos para obtener la funcionalidad que necesite. Ver la Figura 114.
Figura 114. Organización de DLL en clases y uso por parte de ejecutable.

Librería con formato de enlace dinámico

Con la llegada de Windows y sus avances en la gestión de recursos, se desarrolló un nuevo formato de librería basado en la siguiente premisa: dado que un ejecutable no utiliza por norma general todo el código de una librería, resulta más óptimo dejar el código de la misma en su propio archivo, y que el ejecutable enlace con ella en tiempo de ejecución, llamando dinámicamente a las rutinas que necesite. 
Con esta idea se creó el formato de librería de enlace dinámico o DLL (Dynamic Link Library), mediante el cual, durante la fase de generación del ejecutable, lo que se incluye en este de una librería no es el código, sino una referencia o puntero hacia las rutinas que el ejecutable necesita de la DLL; de esta manera, el tamaño de los ejecutables desciende drásticamente al no incluir todo el código de la librería.
El resultado en tiempo de ejecución será el siguiente: cuando el ejecutable encuentre la llamada a un procedimiento residente en una librería de enlace dinámico, dicha rutina será ejecutada directamente desde el archivo .DLL de la librería. La Figura 113 muestra un esquema del proceso.
Figura 113. Proceso de ejecución del código incluyendo librería de enlace dinámico.

Los inicios de las librerías - II

El inconveniente con este mecanismo residía en que el proceso de enlace tomaba todo el código de la librería, fuera utilizado o no por el código objeto, y lo incluía en el ejecutable final, lo que producía ejecutables de enorme tamaño. El esquema de la Figura 112 muestra un ejemplo en el que desde el código objeto se hacen llamadas a las funciones BBB( ) y CCC( ), que residen en la librería. El enlazador toma todo el código de la misma y lo incluye en el ejecutable.
Figura 112. Proceso de enlace de librería en formato LIB, incluyendo todo su código en el ejecutable.

Los inicios de las librerías - I

En los tiempos de la programación para entornos basados en modo texto, y ejecutados bajo MS-DOS, surgió la necesidad antes mencionada, de poder tener nuestro código en un formato que fuera fácil de transportar, pero que ocultara la implementación que habíamos realizado de nuestros procesos. 
Puesto que el archivo ejecutable no servía, ya que se trataba de disponer de un almacén de código que no se pudiera ejecutar directamente, se desarrolló el formato inicial de librería, un archivo con extensión .LIB, que contenía procedimientos y funciones en un estado precompilado. 
Como ya se describió en uno de los temas iniciales del texto, para utilizar el código de una librería clásica desde un programa, tras la fase de compilación del mismo, se tomaba el código objeto producido por el compilador, residente en un archivo con extensión .OBJ, y mediante una utilidad denominada enlazador, se combinaban código objeto y librería para producir el ejecutable final en un archivo con extensión .EXE. Ver Figura 111.
Figura 111. Proceso de enlace del código objeto y librería, para obtener el ejecutable.

Librerías de código (bibliotecas) y ensamblados externos

La reutilización del código 
Durante el desarrollo de un programa, al diseñar y escribir el código de una clase, puede ocurrir que la funcionalidad que esta encierra sea posible aprovecharla en futuros desarrollos, propios o de otros programadores. 
Otra posible situación sería aquella en la que el programador escribe el código, no para una aplicación concreta, sino como un conjunto de utilidades, pensando de manera explícita en su distribución y uso por otros programadores. 
Ante los dos escenarios que acabamos de mencionar, la única manera que hasta el momento conocemos de distribuir a terceros programadores nuestro código para que pueda ser reutilizado, consiste en facilitar los archivos de código fuente, para que sean incorporados al proyecto que lo necesite, y compilados junto al mismo. 
Esto tiene el inconveniente de que facilitamos libremente el acceso a nuestro código fuente, cuando lo único que nos interesa es proporcionar la funcionalidad, pero no el código sobre el que tanto hemos trabajado para conseguirla. 
La solución a esta (delicada en ocasiones) situación podemos encontrarla en las librerías o bibliotecas de código, un aspecto de la programación consistente en compilar nuestro código a un archivo no directamente ejecutable, pero que reside en un formato binario, mediante el cual, un ejecutable típico se puede enlazar para hacer uso del código residente en dicha librería.
Por otro lado, las librerías nos aportan la ventaja de poder tener nuestro código organizado, y ya compilado para ser distribuido.

Modelado visual con UML

El modelado visual es el modelado de una aplicación usando notaciones gráficas. Según Booch, en el libro escrito el año 1999 titulado “Arquitectura del software y el lenguaje de modelado unificado”, es sumamente importante construir el modelo de una aplicación, en este sentido comúnmente se hace la comparación hacia la arquitectura tradicional, en la construcción de casas. Aún cuando la construcción que se planee hacer sea una casa sencilla, el resultado será más satisfactorio si cuenta con todo un respaldo en un correcto diseño. Booch compara la construcción de software con la construcción de una casa para un perro, de una casa para tu familia y de un gran edificio. En el primer caso no será tan evidente la falta de un buen diseño, o al menos la mascota no se quejará demasiado. En el segundo caso, es humanamente posible hacer la construcción de una casa sin los planos adecuados, pero la casa resultante seguramente tendrá varias carencias, o en un peor caso, posiblemente no resistirá ciertas condiciones extremas como un temblor. En el caso del edificio, definitivamente sería un grave error comenzar la construcción sin los estudios y planos adecuados. En el caso del software, es curioso como muchos proyectos del tamaño de un rascacielos, son construidos como si se tratara de la casa de un perro. El modelado visual del software ayuda a capturar las partes esenciales de un sistema: (1) Se utiliza para capturar los procesos de negocios desde la perspectiva del usuario. (2) El modelado visual se utiliza para analizar y diseñar una aplicación, distinguiendo entre los dominios del negocio y los dominios de la computadora. (3) Ayuda a reducir la complejidad. (4) El modelado visual se realiza de manera independiente al lenguaje de implementación. (5) Promueve la reutilización de componentes.
En el libro escrito por la empresa Rational, en el año 1997 titulado “Análisis y diseño con UML”, podría decirse que el lenguaje de modelado unificado es una combinación de: (1) Conceptos de modelado de datos. (2) Modelado del negocio. (3) Modelado de objetos. (4) Modelado de componentes. Precisamente, el nombre del lenguaje de modelado unificado viene del esfuerzo de la compañía Rational de unificar todo el conjunto de métodos de análisis y diseño orientados a objetos. Las ideas principales pertenecen a los investigadores Grady Booch, James Rumbaugh e Ivar Jacobson, quienes trabajan para Rational Software. Además como complemento, también se propuso un proceso unificado, que sirva de complemento al lenguaje de modelado unificado, pero que no sería obligatorio utilizar, fue desarrollado también por Booch, Rumbaugh y Jacobson, y se conoce como “Proceso Unificado Racional”. Se vuelve a recalcar que el lenguaje unificado de modelado es un lenguaje de modelado visual para sistemas software. Los sistemas de software modernos normalmente requieren el soporte de herramientas. Según Arlow y Neustadt, en el libro escrito el año 2006 titulado “UML 2”, los diagramas del lenguaje de modelado unificado son legibles por las personas y las computadoras pueden mostrarlos fácilmente. Con ello, el lenguaje unificado de modelado proporciona una sintaxis visual que se puede utilizar para construir modelos. En muchos proyectos se realiza el modelado visual utilizando los diagramas de casos de uso, los diagramas de clases y los diagramas de secuencias como herramientas de modelado. La descripción de estos diagramas se realiza utilizando el libro escrito el año 2006 por los investigadores Booch, Rumbaugh y Jacobson titulado “El lenguaje de modelado unificado: Guía del usuario”.
En relación con los diagramas de casos de uso, Booch y sus colegas, en el libro citado anteriormente, mencionan que: Ningún sistema se encuentra aislado. Cualquier sistema interesante interactúa con actores humanos o mecánicos que lo utilizan con algún objetivo y que esperan que el sistema funcione de forma predecible. Un caso de uso especifica el comportamiento de un sistema o de una parte de éste, y es una descripción de un conjunto de secuencias de acciones, incluyendo variantes, que ejecuta un sistema para producir un resultado observable de valor para un actor. Los casos de uso se emplean para capturar el comportamiento deseado del sistema en desarrollo, sin tener que especificar cómo se implementa ese comportamiento. Los diagramas de clases brindan solución a esta necesidad al permitir comprender el modelo del negocio a partir de entidades, definiciones y sus relaciones. Un diagrama de secuencia es un diagrama de interacción que resalta la ordenación temporal de los mensajes.

Espacios de nombres (namespaces) - IV

El modo de instanciar, desde Main( ), objetos de las clases del espacio de nombres Contabilidad, es exactamente el mismo que hemos descrito para el espacio de nombres Personal: bien importamos el espacio de nombres, o empleamos los nombres calificados. Veamos el Código fuente 247.

Imports ConsoleApplication1.Contabilidad
Module Module1
Sub Main()
' instanciamos con sintaxis calificada
Dim loCuen As New Contabilidad.Cuenta()
Dim liDatoCuenta As Integer
loCuen.piCodigo = 158
liDatoCuenta = loCuen.Obtener()
' al haber importado el espacio de nombres
' podemos instanciar usando el nombre
' de la clase directamente
Dim loBal As Balance
loBal = New Balance()
loBal.psDescripcion = "Resultado trimestral"
loBal.MostrarDescrip()
Console.ReadLine()
End Sub
End Module
Código fuente 247
Una cualidad muy de agradecer cuando escribimos clases dentro de espacios de nombre, reside en que podemos tener las clases de un mismo espacio de nombres diseminadas por todo el código de la aplicación. Por ejemplo, en el módulo Module1 hemos definido el espacio de nombres Personal, y creado en su interior la clase Empleado; pues bien, si añadimos otra clase al proyecto, podemos incluir también esta clase en el espacio de nombres Personal, a pesar de que dicho código se encuentre en otro fichero distinto. Ver Código fuente 248.

Namespace Personal
Public Class Proveedor
Public psID As Integer
Public psDescrip As String
Public Sub MuestraProv()
Console.WriteLine("El proveedor tiene el código" & _
" {0} y la descripción {1}", Me.psID, Me.psDescrip)
End Sub
End Class
End Namespace
Código fuente 248

Cuando importemos el espacio de nombres Personal, todas las clases que contiene pasarán a estar disponibles, con independencia del fichero de código que las contenga.

Espacios de nombres (namespaces) - III

Debido a que hemos creado una clase dentro de un nuevo espacio de nombres definido en el código, dicho espacio de nombres queda anidado dentro del espacio de nombres raíz del ensamblado. Para instanciar objetos de una clase escrita en un espacio de nombres de esta forma, en primer lugar debemos importar dicho espacio de nombres en la cabecera del fichero de código, utilizando la palabra clave Imports, como se muestra en el Código fuente 244.

' debemos importar el espacio de nombres
' o no podremos instanciar objetos de las
' clases que contiene
Imports ConsoleApplication1.Personal
Module Module1
Sub Main()
' como hemos importado el espacio de nombres Personal
' podemos instanciar un objeto de su clase Empleado
Dim loEmp As Empleado
loEmp = New Empleado()
loEmp.piID = 5
loEmp.MostrarDatos()
Console.ReadLine()
End Sub
End Module
Código fuente 244

Si no utilizamos Imports, también podemos instanciar objetos de clases halladas en espacios de nombres distintos, utilizando en este caso la sintaxis calificada, es decir, escribimos en primer lugar el espacio de nombres, un punto y la clase. El inconveniente de esta forma de codificación, reside en que cada vez que declaremos e instanciemos un objeto tenemos que emplear esta sintaxis calificada, por lo cuál, es mucho más cómodo importar el espacio de nombres al comienzo del fichero. Ver Código fuente 245.

Dim loEmp As Personal.Empleado
loEmp = New Personal.Empleado()
Código fuente 245
Finalmente, vamos a agregar una nueva clase al proyecto, a la que daremos el nombre Gestion.VB. Sin embargo no utilizaremos la clase que crea por defecto, borraremos todo el código de ese fichero y escribiremos dos nuevas clases en él: Cuenta y Balance, que además, estarán contenidas en el espacio de nombres Contabilidad. De esta forma queda demostrado cómo podemos organizar nuestro código, además de en clases, en espacios de nombre que contengan clases con funcionalidades similares. Ver Código fuente 246.

Namespace Contabilidad
Public Class Cuenta
Public piCodigo As Integer
Public Function Obtener() As Integer
Return Me.piCodigo
End Function
End Class
Public Class Balance
Public psDescripcion As String
Public Sub MostrarDescrip()
Console.WriteLine("La descripción del balance es: {0}",
Me.psDescripcion)
Console.ReadLine()
End Sub
End Class
End Namespace
Código fuente 246

Espacios de nombres (namespaces) - II

Como muestra la imagen, tanto el ensamblado como su espacio de nombres tienen como nombre ConsoleApplication1, por lo que todas las clases que escribamos dentro de este proyecto estarán dentro de dicho espacio de nombres. 
Vamos a ir construyendo progresivamente un ejemplo, para ver las variantes de uso de clases en función del espacio de nombres en el que estén contenidas. Crearemos para ello una nueva aplicación de consola, y en el fichero de código que incluye por defecto, además del módulo Module1 ya incluido al crearse el proyecto, escribiremos la clase Factura, ver Código fuente 242.

Module Module1
Sub Main()
' como la clase Factura se encuentra
' en el espacio de nombres raíz,
' instanciamos normalmente
Dim loFac As New Factura()
loFac.piID = 5
loFac.piImporte = 200
loFac.Datos()
Console.ReadLine()
End Sub
End Module
' clase Factura
' esta clase se encuentra dentro
' del espacio de nombres raíz del ensamblado
Public Class Factura
Public piID As Integer
Public piImporte As Integer
Public Sub Datos()
Console.WriteLine("La factura {0}, tiene un importe de {1}", _
Me.piID, Me.piImporte)
End Sub
End Class
Código fuente 242
Seguidamente, y en el mismo fichero de código, creamos la clase Empleado, pero la incluimos en el espacio de nombres Personal. Para crear un espacio de nombres en el código de la aplicación debemos utilizar las palabras clave Namespace...End Namespace. Ver Código fuente 243.
' clase Empleado
' esta clase se encuentra dentro
' del espacio de nombres raíz del ensamblado,
' y a su vez, dentro del espacio de
' nombres Personal
Namespace Personal
Public Class Empleado
Public psID As Integer
Public Sub MostrarDatos()
Console.WriteLine("Identificador del empleado: {0}", Me.psID)
Console.ReadLine()
End Sub
End Class
End Namespace
Código fuente 243

Espacios de nombres (namespaces) - I

Un espacio de nombres, también denominado namespace, es un contenedor lógico de código, que nos permite organizar de un modo más óptimo, las clases dentro de un proyecto o ensamblado. El concepto de ensamblado será explicado en un apartado posterior, por el momento podemos equiparar la noción de ensamblado con la de proyecto. 
En el presente apartado, dedicaremos nuestros esfuerzos al trabajo con los espacios de nombres desde su vertiente práctica, es decir, veremos cómo se utilizan los espacios de nombres en un proyecto para agrupar funcionalmente las clases que contiene. Para ello vamos a desarrollar el proyecto de ejemplo ConsoleApplication1 (hacer clic aquí para acceder a este ejemplo).
Cada vez que creamos un nuevo proyecto en VB.NET, se crea un espacio de nombres a nivel del ensamblado, con su mismo nombre, y que engloba a todos los tipos o clases que vayamos creando. Este espacio de nombres recibe la denominación de espacio de nombres raíz, y podemos verlo abriendo la ventana de propiedades del proyecto. Ver Figura 110.
Figura 110. Nombre del espacio de nombres raíz en las propiedades del proyecto / ensamblado.

Enlace tardío - II

Por ejemplo, si aparte de nuestra conocida clase Empleado, escribimos otra nueva llamada Proveedor, con algunos aspectos similares, como las propiedades Nombre, Apellidos, el método MostrarDatos( ), etc., podremos utilizar una misma variable para manipular cada uno de los objetos que instanciemos de estas clases; evidentemente, tendremos que asignar el objeto pertinente a la variable antes de poder manejarlo. 
Vamos incluso a crear otra clase más, llamada Horario, con un método que devuelva la hora actual del sistema, y ejecutaremos dicho método asignando un objeto de esta clase a la misma variable utilizada para manejar los objetos Empleado y Proveedor. Veamos todo en el Código fuente 241.

Module General
Sub Main()
' tipificamos como Object,
' por lo que obtendremos enlace tardío
Dim loVariosObj As Object
' instanciamos un objeto de Empleado
loVariosObj = New Empleado()
loVariosObj.Nombre = "Juan"
loVariosObj.Apellidos = "Rollo"
loVariosObj.MostrarDatos()
' instanciamos un objeto de Proveedor
loVariosObj = New Proveedor()
loVariosObj.Nombre = "Alicia"
loVariosObj.Apellidos = "Cañaveral"
loVariosObj.MostrarDatos()
' instanciamos un objeto de Horario
loVariosObj = New Horario()
loVariosObj.HoraActual()
End Sub
End Module
Public Class Empleado
Private msNombre As String
Private msApellidos As String
Public Property Nombre() As String
Get
Return msNombre
End Get
Set(ByVal Value As String)
msNombre = Value
End Set
End Property
Public Property Apellidos() As String
Get
Return msApellidos
End Get
Set(ByVal Value As String)
msApellidos = Value
End Set
End Property
Public Sub MostrarDatos()
Console.WriteLine("El empleado seleccionado es: {0} {1}", _
msNombre, msApellidos)
Console.ReadLine()
End Sub
End Class
Public Class Proveedor
Private msNombre As String
Private msApellidos As String
Public Property Nombre() As String
Get
Return msNombre
End Get
Set(ByVal Value As String)
msNombre = Value
End Set
End Property
Public Property Apellidos() As String
Get
Return msApellidos
End Get
Set(ByVal Value As String)
msApellidos = Value
End Set
End Property
Public Sub MostrarDatos()
Console.WriteLine("El proveedor actual es: {0} {1}", _
msNombre, msApellidos)
Console.ReadLine()
End Sub
End Class
Public Class Horario
Public Sub HoraActual()
Console.WriteLine("Hora del sistema: {0}", Format(Now(), "HH:mm"))
Console.ReadLine()
End Sub
End Class
Código fuente 241
De cara a próximos apartados referentes a la herencia, tengamos en cuenta la siguiente regla respecto a los tipos de enlace. 
El enlace temprano se basa en el tipo de la referencia o clase establecida al declarar la variable, mientras que el enlace tardío se basa en el tipo del propio objeto asignado a la variable, sin tener en cuenta la clase con que haya sido declarada la variable.

Enlace tardío - I

También conocido como late binding o dynamic binding, este enlace establece que las referencias entre la variable y el objeto que contiene van a ser resueltas en tiempo de ejecución. El principal inconveniente en este tipo de enlace radica en que el código generado será más lento, ya que desconoce con qué miembros de objeto tendrá que trabajar, debiendo averiguar esta información durante la ejecución del programa. 
Adicionalmente, el trabajo del programador será también mayor, ya que tendrá que conocer con antelación, la lista de miembros o interfaz que implementa el objeto. Como ventaja nos aporta una mayor flexibilidad, ya que con la misma variable podemos manipular objetos de distinto tipo. Para ello, tendremos que tipificar la variable como Object. Ver Figura 109.
Figura 109. Esquema de funcionamiento del enlace tardío de objetos.

Enlace temprano

También conocido como early binding o static binding, este enlace establece que las referencias entre la variable y el objeto que contiene van a ser resueltas en tiempo de compilación. 
El enlace temprano se realiza en el momento de declarar la variable, asignándole a esta el tipo de objeto con el que va a trabajar. Con ello conseguimos un mejor rendimiento del programa, puesto que el código generado, al conocer de forma precisa qué propiedades y métodos debe usar, se ejecutará de modo más veloz. En nuestros anteriores ejemplos con la clase Empleado, al declarar una variable de dicha clase, tenemos desde ese momento, acceso directo a todos sus miembros. Ver Figura 107.
Figura 107. Esquema de funcionamiento del enlace temprano de objetos.
Además, y como habrá podido comprobar hasta ahora el lector, la escritura de código mediante enlace temprano también se facilita, ya que en todo momento, los asistentes del IDE muestran las listas de miembros disponibles para el objeto que estemos codificando. Ver Figura 108
Figura 108. Lista de miembros de un objeto en el editor de código.
El enlace temprano, debido a su mejor rendimiento, es el tipo de enlace utilizado por defecto dentro del CLR.

Enlace (binding) de variables a referencias de objetos

El enlace, también denominado binding, es un proceso que determina cuándo va a ser efectuada la localización de los miembros de un objeto, por parte de la variable que va a manipular dicho objeto. Existen dos tipos de enlace, los cuales describimos a continuación.

Sobrecarga de métodos o polimorfismo, en una misma clase

La sobrecarga de métodos, tal y como ya vimos en el apartado acerca de la sobrecarga de procedimientos en los aspectos básicos del lenguaje, es una técnica que consiste en crear varios métodos con idéntico nombre dentro de la misma clase, distinguiéndose entre sí por su lista de parámetros. 
Para declarar un método como sobrecargado, debemos utilizar la palabra clave Overloads después del modificador de ámbito. Podemos sobrecargar métodos de tipo Sub y Function. Una situación que podría requerir la sobrecarga de métodos sería la siguiente: la clase Empleado necesita manejar en diferentes formas, la información que referente al sueldo existe sobre el empleado. 
Por tal motivo, vamos a crear tres métodos con el nombre Sueldo( ), que variarán en su firma, o protocolo de llamada, y realizarán diferentes tareas, pero todas ellas relacionadas con el sueldo del empleado. Veamos el Código fuente 240.

Module General
Sub Main()
Dim loEmpleado As New Empleado()
Dim ldbResultadoIncent As Double
loEmpleado.Salario = 1020.82
'llamada al primer método sobrecargado
loEmpleado.Sueldo()
'llamada al segundo método sobrecargado
Console.WriteLine("El sueldo se transferirá el día {0}", _
loEmpleado.Sueldo(29))
'llamada al tercer método sobrecargado
ldbResultadoIncent = loEmpleado.Sueldo(50.75, "Extras")
Console.WriteLine("El incentivo a pagar será {0}", ldbResultadoIncent)
Console.ReadLine()
End Sub
End Module
Public Class Empleado
Private mdbSalario As Double
Public Property Salario() As Double
Get
Return mdbSalario
End Get
Set(ByVal Value As Double)
mdbSalario = Value
End Set
End Property
' métodos sobrecargados
Public Overloads Sub Sueldo()
' aquí mostramos en consola el importe del sueldo formateado
Console.WriteLine("El sueldo es {0}", Format(Me.Salario, "#,#.##"))
Console.ReadLine()
End Sub
Public Overloads Function Sueldo(ByVal liDia As Integer) As String
' aquí mostramos la fecha del mes actual
' en la que se realizará la transferencia
' del sueldo al banco del empleado
Dim ldtFechaActual As Date
Dim lsFechaCobro As String
ldtFechaActual = Now()
lsFechaCobro = CStr(liDia) & "/" & _
CStr(Month(ldtFechaActual)) & "/" & _
CStr(Year(ldtFechaActual))
Return lsFechaCobro
End Function
Public Overloads Function Sueldo(ByVal ldbImporteIncentivo As Double, _
ByVal lsTipoIncentivo As String) As Double
' aquí calculamos la cantidad de incentivo
' que se añadirá al sueldo del empleado,
' en función del tipo de incentivo
Dim ldbIncentivo As Double
' según el tipo de incentivo,
' se descuenta un importe
' de la cantidad del incentivo
Select Case lsTipoIncentivo
Case "Viajes"
ldbIncentivo = ldbImporteIncentivo - 30
Case "Extras"
ldbIncentivo = ldbImporteIncentivo - 15
End Select
Return ldbIncentivo
End Function
End Class
Código fuente 240
Vemos pues, cómo a través de la sobrecarga conseguimos también polimorfismo para una clase, ya que el mismo nombre de método, en función de los parámetros pasados, actuará de diferente forma. 
A pesar de haber indicado que la palabra clave Overloads nos permite sobrecargar los métodos con nombres iguales en la clase, realmente no sería necesario su uso, ya que el compilador detecta la diferencia entre dichos métodos a través de su lista de parámetros. Sin embargo se recomienda el uso de esta palabra clave por motivos de legibilidad del código, de forma que nos ayude a reconocer más rápidamente los métodos sobrecargados. 
Cuando realmente necesitaremos emplear Overloads será al sobrecargar un método en una clase derivada, aspecto este que se explicará en un próximo apartado.

Uso de Me para llamar a los miembros de la propia clase

Cuando desde el código de una clase queramos hacer referencia a un miembro de la propia clase: campo, propiedad o método, podemos utilizar la palabra clave Me para manipular dicho elemento. Veamos el Código fuente 239.

Module Module1
Sub Main()
Dim loEmp As New Empleado()
loEmp.piID = 980
loEmp.Nombre = "Almudena Bosque"
loEmp.VerDatos()
Console.ReadLine()
End Sub
End Module
Public Class Empleado
Public piID As Integer
Private msNombre As String
Public Property Nombre() As String
Get
Return msNombre
End Get
Set(ByVal Value As String)
msNombre = Value
End Set
End Property
Public Sub VerDatos()
' utilizamos Me en este método para llamar al
' método NombreMay() que está en la misma clase
Console.WriteLine("Nombre del empleado: {0}", Me.NombreMay())
End Sub
Public Function NombreMay() As String
Return UCase(msNombre)
End Function
End Class
Código fuente 239
Como acabamos de ver, desde el código de la propia clase Empleado llamamos a un método situado en la propia clase, anteponiendo la palabra clave Me. Aunque el uso de Me no es obligatorio, ya que el compilador reconoce el miembro que queremos ejecutar, sí es recomendable ya que facilita la lectura de nuestro código.

Resultados distintos en objetos de la misma clase

La capacidad de instanciar al mismo tiempo varios objetos de la misma clase nos lleva a una interesante cuestión: la obtención de resultados distintos a partir de objetos del mismo tipo, cuando dichos objetos tienen datos diferentes en sus propiedades, ya que aunque el código ejecutado es el mismo, los valores de sus propiedades difieren entre sí. 
Un ejemplo ilustrativo de esta situación sería la creación de dos objetos de la clase Empleado, en los que cada uno tuviera fechas de comienzo y días de vacaciones distintos. En este caso, aunque los objetos son del mismo tipo, la finalización de sus vacaciones sería distinta. Ver el Código fuente 238.

Dim loEmpleado1 As Empleado
Dim loEmpleado2 As Empleado
loEmpleado1 = New Empleado()
loEmpleado2 = New Empleado()
loEmpleado1.InicioVacaciones = "25/07/2002"
loEmpleado1.DiasVacaciones = 20
loEmpleado2.InicioVacaciones = "25/07/2002"
loEmpleado2.DiasVacaciones = 30
' los dos objetos son de la clase Empleado,
' pero el resultado en este caso al usar la
' propiedad FinVacaciones no será igual
' para estos objetos, dados los diferentes
' valores de algunas de sus propiedades
Código fuente 238

La estructura With...End With

Este elemento del lenguaje nos facilita la escritura de código cuando hacemos referencia a los miembros de un objeto, ya que nos ahorra tener que escribir el nombre del objeto, siendo preciso indicar sólo sus miembros. La sintaxis de esta estructura se muestra en el Código fuente 235.

With Objeto
.Campo
.Propiedad
.Método()
End UIT
Código fuente 235
Pongamos como ejemplo, que hemos creado una clase con el nombre Empleado que tiene las propiedades Nombre, Apellidos, y el método MostrarDatos( ), para manipular un objeto de esta clase mediante With, lo haríamos como muestra el Código fuente 236.

Dim loEmp As Empleado = New Empleado()
With loEmp
.Nombre = "Ana"
.Apellidos = "Naranjo"
.MostrarDatos()
End UIT
Código fuente 236
Podemos también anidar esta estructura, con el fin de manipular más de un objeto, veamos el Código fuente 237.

Dim loEmp As Empleado = New Empleado()
Dim loUsu As New Usuario()
With loEmp
.Nombre = "Ana"
.Apellidos = "Naranjo"
.MostrarDatos()
With loUsu
.AsignarNombre("Jacinto")
End With
End With
Código fuente 237

¿Cuándo crear una propiedad y cuándo un método?

Debido a que una propiedad, a través de su procedimiento Property asociado, puede ejecutar código, la decisión de escribir cierta operación en una clase empleando una propiedad o un método, es en algunas ocasiones difícil, ya que existen procesos que pueden ser resueltos utilizando ambos modos. 
Sin ir más lejos, el método CalcularVacaciones( ), visto en el ejemplo del apartado anterior, bien podría haberse resuelto a través de una propiedad, como muestra el Código fuente 234. En él hemos incluido sólo las partes modificadas de la clase Empleado para solucionar este problema

Module General
Sub Main()
' crear objeto Empleado
Dim loEmpleado As Empleado
loEmpleado = New Empleado()
' asignar valores a propiedades
loEmpleado.Identificador = 78
loEmpleado.Nombre = "Antonio"
loEmpleado.Apellidos = "Iglesias"
' esta sería la parte nueva en el código cliente:
' asignar la fecha de inicio y número de días
' de vacaciones, y obtener de la propiedad FinVacaciones
' el día en que termina las vacaciones, aplicando
' en este caso, un formato a la fecha obtenida
loEmpleado.InicioVacaciones = "20/07/2002"
loEmpleado.DiasVacaciones = 15
Console.WriteLine("El empleado {0} - {1} {2}" & ControlChars.CrLf & _
"finaliza sus vacaciones el día {3}", _
loEmpleado.Identificador, loEmpleado.Nombre, _
loEmpleado.Apellidos, _
Format(loEmpleado.FinVacaciones, "d-MMMM-yy"))
Console.ReadLine()
End Sub
End Module
Public Class Empleado
' en esta clase creamos 3 propiedades nuevas,
' para guardar la fecha de inicio de vacaciones,
' los días y la fecha de fin
' variables de propiedad
' .....
' .....
Private mdtInicioVacaciones As Date
Private mdtFinVacaciones As Date
Private miDiasVacaciones As Integer
' procedimientos de propiedad
' .....
' .....
Public Property InicioVacaciones() As Date
Get
Return mdtInicioVacaciones
End Get
Set(ByVal Value As Date)
mdtInicioVacaciones = Value
End Set
End Property
Public Property DiasVacaciones() As Integer
Get
Return miDiasVacaciones
End Get
Set(ByVal Value As Integer)
miDiasVacaciones = Value
End Set
End Property
' en este procedimiento de propiedad
' realizamos el cálculo para obtener
' la fecha de fin de vacaciones y
' devolvemos dicha fecha al código cliente
Public ReadOnly Property FinVacaciones() As Date
Get
' calcular la fecha de fin de vacaciones
Return DateAdd(DateInterval.Day, _
DiasVacaciones, InicioVacaciones)
End Get
End Property
' .....
' .....
End Class
Código fuente 234
Queda por lo tanto, en manos del programador, determinar el criterio por el cuál un proceso se resolverá mediante una propiedad o un método, debiendo ser una decisión flexible y no basarse en unas normas rígidas.

Métodos y espacios de nombre - III

Gracias a que la codificación de todos los procesos reside ahora en la clase, el código cliente que tenga que tratar ahora con el empleado, quedaría simplificado y reducido a lo que se muestra en el Código fuente 233.

Module General
Sub Main()
' instanciar objeto
Dim loEmpleado As New Empleado()
loEmpleado.Identificador = 850
loEmpleado.Nombre = "Juan"
loEmpleado.Apellidos = "García"
' asignar resto de propiedades
' .....
' .....
' llamar a sus métodos
loEmpleado.MostrarEmpleado()
loEmpleado.TransfNomina()
' .....
' .....
End Sub
End Module
Código fuente 233

Hemos podido comprobar lo sencillo e intuitivo que resulta trabajar con determinados procesos a través de técnicas OOP, ya que una vez codificada la clase, tan sólo hemos de hacer uso de ella instanciando el correspondiente objeto; con la ventaja añadida de que podemos tener varios objetos de la misma clase funcionando al mismo tiempo.

Métodos y espacios de nombre - II

Llegados a este punto, hemos completado todos los pasos elementales en cuanto a la creación de una clase. Retomemos pues, el caso del ejemplo expuesto anteriormente, de manera que si sustituimos el enfoque procedural de los procesos del empleado, por uno orientado a objeto, la clase Empleado resultante podría ser algo similar a la mostrada en el Código fuente 232.

Public Class Empleado
' variables de propiedad
Private miID As Integer
Private msNombre As String
Private msApellidos As String
Private msDNI As String
Private mdtFechaAlta As Date
Private mdbSueldo As Double
Private mdtInicioVacaciones As Date
Private miDiasVacaciones As Integer
' procedimientos de propiedad
Public Property Identificador() As Integer
Get
Return miID
End Get
Set(ByVal Value As Integer)
miID = Value
End Set
End Property
Public Property Nombre() As String
Get
Return msNombre
End Get
Set(ByVal Value As String)
msNombre = Value
End Set
End Property
Public Property Apellidos() As String
Get
Return msApellidos
End Get
Set(ByVal Value As String)
msApellidos = Value
End Set
End Property
Public Property DNI() As String
Get
Return msDNI
End Get
Set(ByVal Value As String)
msDNI = Value
End Set
End Property
Public Property FechaAlta() As Date
Get
Return mdtFechaAlta
End Get
Set(ByVal Value As Date)
mdtFechaAlta = Value
End Set
End Property
Public Property Sueldo() As Double
Get
Return mdbSueldo
End Get
Set(ByVal Value As Double)
mdbSueldo = Value
End Set
End Property
Public Property InicioVacaciones() As Date
Get
Return mdtInicioVacaciones
End Get
Set(ByVal Value As Date)
mdtInicioVacaciones = Value
End Set
End Property
Public Property DiasVacaciones() As Integer
Get
Return miDiasVacaciones
End Get
Set(ByVal Value As Integer)
miDiasVacaciones = Value
End Set
End Property
Public Sub CalcularVacaciones()
' en este método calculamos el periodo
' de vacaciones del empleado,
' mostrando los resultados en consola
Dim ldtFinal As Date
ldtFinal = DateAdd(DateInterval.Day, miDiasVacaciones, mdtInicioVacaciones)
Console.WriteLine("Empleado {0} - {1} {2}", _
miID, msNombre, msApellidos)
Console.WriteLine("Vacaciones desde {0} hasta {1}", _
Format(mdtInicioVacaciones, "dd/MMM/yy"), _
Format(ldtFinal, "d/MMMM/yyyy"))
Console.ReadLine()
End Sub
Public Sub CrearEmpleado()
' crear un nuevo registro en la base de datos,
' grabar los valores que debe haber
' en las propiedades
' .......
Console.WriteLine("Se ha grabado el empleado: {0} - {1} {2}", _
miID, msNombre, msApellidos)
Console.ReadLine()
End Sub
Public Sub TransfNomina()
' realizamos la transferencia de nómina
' a un empleado, utilizando su identificador
' ......
' obtener los datos del empleado de la base de datos
' y traspasarlos a las propiedades
' ......
' visualizamos el resultado
Console.WriteLine("Pago de nómina")
Console.WriteLine("Empleado: {0} {1}", msNombre, msApellidos)
Console.WriteLine("Ingresado: {0}", mdbSueldo)
Console.ReadLine()
End Sub
Public Sub MostrarEmpleado()
' buscar la información del empleado en la base de datos
' usando el valor de la propiedad identificador
Dim lsDatosEmpleado As String
' ......
Console.WriteLine("El empleado seleccionado es: {0}", msNombre,
msApellidos)
Console.ReadLine()
End Sub
End Class
Código fuente 232

Métodos y espacios de nombre - I

Creación de métodos para la clase 
Para crear un método en una clase debemos escribir un procedimiento de tipo Sub o Function, en función de si necesitamos devolver o no, un valor desde el método. Por este motivo, podemos deducir que un método es lo mismo que un procedimiento, siendo las diferencias existentes entre ambos tan sólo a nivel conceptual: mientras que a una rutina de código dentro de un módulo se le denomina procedimiento, si la escribimos dentro de una clase se le denomina método. 
Los métodos, tal y como explicamos en los primeros apartados teóricos sobre OOP, son aquellos miembros de una clase que definen el comportamiento de los objetos, como consecuencia de las acciones que llevan a cabo al ser ejecutados. 
Veamos a continuación, un ejemplo concreto de creación de método. En la clase Empleado necesitamos realizar un cálculo del día en que va a finalizar un empleado sus vacaciones; para ello precisamos conocer la fecha de comienzo y la cantidad de días que va a estar de vacaciones, por lo que escribiremos un método en nuestra clase al que llamaremos CalcularVacaciones( ); a este método le pasaremos los parámetros de la fecha de inicio y el número de días, devolviendo, al ser de tipo Function, la fecha de finalización del periodo vacacional.

Module General
Sub Main()
' instanciar objeto Empleado
Dim loEmpleado As Empleado
loEmpleado = New Empleado()
' asignar valores a propiedades
loEmpleado.Identificador = 78
loEmpleado.Nombre = "Antonio"
loEmpleado.Apellidos = "Iglesias"
' llamar a método
loEmpleado.CalcularVacaciones("20/07/2002", 15)
End Sub
End Module
Public Class Empleado
' variables de propiedad
Private miID As Integer
Private msNombre As String
Private msApellidos As String
' procedimientos de propiedad
Public Property Identificador() As Integer
' ......
End Property
Public Property Nombre() As String
' ......
End Property
Public Property Apellidos() As String
' ......
End Property
' métodos
Public Sub CalcularVacaciones(ByVal ldtInicio As Date, _
ByVal liDias As Integer)
' en este método calculamos el periodo
' de vacaciones del empleado,
' mostrando los resultados en consola
Dim ldtFinal As Date
ldtFinal = DateAdd(DateInterval.Day, liDias, ldtInicio)
Console.WriteLine("Empleado {0} - {1} {2}", _
Identificador, Nombre, Apellidos)
Console.WriteLine("Vacaciones desde {0} hasta {1}", _
Format(ldtInicio, "dd/MMM/yy"), _
Format(ldtFinal, "d/MMMM/yyyy"))
Console.ReadLine()
End Sub
End Class
Código fuente 231

Propiedades predeterminadas

Una propiedad predeterminada o por defecto, es aquella que nos permite su manipulación omitiendo el nombre. Al declarar una propiedad por defecto, debemos utilizar la palabra clave Default al comienzo de la sentencia de declaración, siendo obligatorio además, que dicho procedimiento de propiedad reciba al menos un parámetro. 
La ventaja a la hora de trabajar con una propiedad de este tipo en un objeto, reside en que no necesitamos indicar su nombre, sólo es preciso especificar su parámetro. 
Debido a la naturaleza de este tipo de propiedad, sólo es posible crear una propiedad predeterminada en cada clase. Una de las situaciones más idóneas en una clase para crear una propiedad predeterminada, serían aquellos elementos en los que tengamos que manejar un conjunto de valores a través de un array, es decir, la variable de propiedad sería el array que manipularíamos a través del correspondiente procedimiento de propiedad. De esta manera, para asignar y obtener valores de este tipo de propiedad, tendremos que utilizar el índice del array que internamente la gestiona. 
Pongamos como ejemplo, el hecho de que el trabajo desempeñado por el empleado le supone realizar viajes a diferentes ciudades; para llevar un control de los viajes realizados, crearemos una nueva propiedad, que además será predeterminada. Veamos este ejemplo en el Código fuente 230.

Module General
Sub Main()
Dim loEmpleado As New Empleado()
Dim liContador As Integer
' primero manejamos la propiedad predeterminada
' igual que una normal
loEmpleado.Viajes(0) = "Valencia"
' aquí manipulamos la propiedad predeterminada
' sin indicar su nombre
loEmpleado(1) = "Toledo"
For liContador = 0 To 1
Console.WriteLine("Visita: {0} - Ciudad: {1}", _
liContador, loEmpleado(liContador))
Next
Console.ReadLine()
End Sub
End Module
Public Class Empleado
' este es el array asociado a
' la propiedad predeterminada
Private msViajes() As String
' declaración de la propiedad predeterminada
Default Public Property Viajes(ByVal Indice As Integer) As String
Get
' para devolver un valor, empleamos
' el número de índice pasado
' como parámetro
Return msViajes(Indice)
End Get
Set(ByVal Value As String)
' para asignar un valor a la propiedad,
' comprobamos primero si el array está vacío
' comprobar si el array está vacío,
' al ser el array también un objeto,
' utilizamos el operador Is
If msViajes Is Nothing Then
ReDim msViajes(0)
Else
' si el array ya contenía valores,
' añadir un nuevo elemento
ReDim Preserve msViajes(UBound(msViajes) + 1)
End If
' asignar el valor al array
msViajes(Indice) = Value
End Set
End Property
End Class
Código fuente 230

El uso de propiedades predeterminadas proporciona una cierta comodidad a la hora de escribir el código, sin embargo, si nos acostumbramos a especificar en todo momento las propiedades en el código, ganaremos en legibilidad.

Nombres de propiedad más naturales

Cuando desde código cliente trabajamos con objetos, estos ofrecen habitualmente nombres de propiedades claros y sin notaciones. En el caso de la clase Empleado tenemos un inconveniente a este respecto con el campo de clase correspondiente al nombre del empleado, ya que en él utilizamos convenciones de notación para facilitar el mantenimiento del código, pero por otra parte, estamos contribuyendo a dificultar la legibilidad de los miembros de la clase desde el código cliente. 
Es cierto que podemos obviar las convenciones de notación en el código, pero esto, como ya comentamos en el apartado sobre convenciones de código, puede hacer que la lectura del programa sea más complicada. Como hemos comprobado también en los pasados ejemplos, si utilizamos propiedades, podemos mantener nuestras normas de notación en cuanto a las variables de la clase, sea cual sea su tipo, y ofrecer al código cliente, nombres más naturales a través de los procedimientos Property. 
Por lo tanto, si en lugar de utilizar un campo de clase para el nombre del empleado, la convertimos en una propiedad, habremos ganado en claridad de cara al programador usuario de nuestra clase. Veámoslo en el Código fuente 229.

Module General
Sub Main()
Dim loEmpleado As New Empleado()
' al utilizar un objeto desde código cliente
' siempre es más sencillo manipular la
' propiedad Nombre, que msNombre, en cuanto
' a claridad del código se refiere
loEmpleado.Nombre = "Juan"
End Sub
End Module
Public Class Empleado
' antes usábamos un campo de clase...
'Public psNombre As String <---
' ...pero lo convertimos en una variable de propiedad...
Private msNombre As String
' ...creando su procedimiento de propiedad correspondiente
Public Property Nombre() As String
Get
Return msNombre
End Get
Set(ByVal Value As String)
msNombre = Value
End Set
End Property
End Class
Código fuente 229

Propiedades virtuales

Otra de las ventajas del uso de propiedades reside en la posibilidad de definir propiedades virtuales; es decir, una propiedad que no tenga una correspondencia directa con una variable de propiedad, ya que podemos crear un procedimiento Property que no esté obligatoriamente asociado con una variable. Siguiendo con la clase Empleado, en esta ocasión creamos una propiedad para almacenar la fecha en la que el empleado ha sido incorporado a la empresa; esto no entraña ninguna novedad. Sin embargo, seguidamente necesitamos disponer de una propiedad que nos permita mostrar el nombre del mes en el que se ha dado de alta al empleado. 
Podemos resolver esta cuestión creando una variable de propiedad, guardando en ella una cadena con el nombre del mes; pero si disponemos de la fecha de alta, que ya contiene el mes, nos ahorraremos ese trabajo extra creando una propiedad, en este caso de sólo lectura, en la que extraigamos el nombre del mes de la fecha de alta y lo devolvamos como resultado. Veamos como hacerlo en el Código fuente 228.

Module General
Sub Main()
Dim loEmpleado As Empleado
loEmpleado = New Empleado()
loEmpleado.psNombre = "Antonio"
loEmpleado.FechaAlta = "12/6/2002"
' mostramos el mes de alta, que corresponde
' a una propiedad virtual del objeto
Console.WriteLine("El empleado {0} se ha dado de alta en el mes de {1}", _
loEmpleado.psNombre, loEmpleado.MesAlta)
Console.ReadLine()
End Sub
End Module
Public Class Empleado
' campo de clase
Public psNombre As String
' variables de propiedad
Private mdtFechaAlta As Date
' propiedad para manejar la fecha
' de alta del empleado
Public Property FechaAlta() As Date
Get
Return mdtFechaAlta
End Get
Set(ByVal Value As Date)
mdtFechaAlta = Value
End Set
End Property
' propiedad virtual
' en ella devolvemos el nombre del mes en el que se ha dado
' de alta al empleado, utilizando la variable de otra propiedad
Public ReadOnly Property MesAlta() As String
Get
Return Format(mdtFechaAlta, "MMMM")
End Get
End Property
End Class
Código fuente 228

Propiedades de sólo lectura o sólo escritura

Se nos plantea ahora un nuevo caso para nuestra clase Empleado: debemos guardar el valor del código de cuenta bancaria del empleado en el objeto, pero sin permitir que dicha información sea accesible desde el código cliente. Igualmente y en función de los primeros dígitos de la cuenta bancaria, necesitamos mostrar el nombre de la entidad, pero sin permitir al código cliente su modificación, ya que esta va a ser siempre una operación que debe calcular el código de la clase. 
Utilizando campos de clase no es posible resolver esta situación, ya que al ser de ámbito público, permiten tanto la escritura como lectura de sus valores. Pero si empleamos propiedades, estas nos permiten la creación de las denominadas propiedades de sólo lectura o sólo escritura, en las que utilizando las palabras clave ReadOnly y WriteOnly, conseguimos que a una determinada propiedad, sólo podamos asignarle o recuperar su valor. Debido a esto, en una propiedad ReadOnly no podremos escribir el bloque Set, ya que no tendría sentido, puesto que no se va a utilizar. 
Lo mismo podemos aplicar para una propiedad WriteOnly, sólo que en esta, el bloque que no podremos codificar será Get. Igualmente obtendremos un error del compilador, si en el código cliente intentamos asignar un valor a una propiedad ReadOnly, u obtener un valor de una propiedad WriteOnly.
Veamos a continuación, en el Código fuente 227, un ejemplo de cómo resolver el problema comentado al comienzo de este apartado.

Module General
Sub Main()
Dim loEmpleado As Empleado
loEmpleado = New Empleado()
loEmpleado.psNombre = "Pedro"
' a esta propiedad sólo podemos asignarle
' valor, si intentamos obtenerlo, se producirá
' un error
loEmpleado.CuentaBancaria = "2222-56-7779995555"
' en esta línea, la propiedad EntidadBancaria sólo
' nos permite obtener valor, si intentamos asignarlo
' se producirá un error
Console.WriteLine("La entidad del empleado {0} es {1}", _
loEmpleado.psNombre, loEmpleado.EntidadBancaria)
Console.ReadLine()
End Sub
End Module
Public Class Empleado
' campo de clase
Public psNombre As String
' variables de propiedad
Private msCtaBancaria As String
Private msEntidad As String
' variables diversas
Private msCodigoEntidad As String
' esta propiedad sólo permite asignar valores,
' por lo que no dispone de bloque Get
Public WriteOnly Property CuentaBancaria() As String
Set(ByVal Value As String)
Select Case Left(Value, 4)
Case "1111"
msEntidad = "Banco Universal"
Case "2222"
msEntidad = "Banco General"
Case "3333"
msEntidad = "Caja Metropolitana"
Case Else
msEntidad = "entidad sin catalogar"
End Select
End Set
End Property
' esta propiedad sólo permite obtener valores,
' por lo que no dispone de bloque Set
Public ReadOnly Property EntidadBancaria() As String
Get
Return msEntidad
End Get
End Property
End Class
Código fuente 227

Encapsulación a través de propiedades - II

¿Que está sucediendo aquí?. Hemos creado un objeto empleado al que le hemos dado categoría 1, sin embargo le estamos asignando un sueldo que no corresponde a su categoría, pero se nos permite hacerlo sin ningún problema, ya que no existe un medio de control que nos lo impida. 
Afrontando el problema mediante el uso de propiedades, contamos con la ventaja de escribir código de validación en los correspondientes procedimientos Property; con ello encapsulamos el código de la clase, manteniéndolo a salvo de asignaciones incoherentes. Veamos esta solución en el Código fuente 226.

Module General
Sub Main()
Dim loEmpleado As Empleado
loEmpleado = New Empleado()
loEmpleado.psNombre = "Pedro"
loEmpleado.Categoria = 1
loEmpleado.Sueldo = 250
Console.WriteLine("Asignación incorrecta")
Console.WriteLine("Empleado {0} - Categoria {1} - Sueldo {2}", _
loEmpleado.psNombre, loEmpleado.Categoria, loEmpleado.Sueldo)
loEmpleado.Sueldo = 175
Console.WriteLine("Asignación correcta")
Console.WriteLine("Empleado {0} - Categoria {1} - Sueldo {2}", _
loEmpleado.psNombre, loEmpleado.Categoria, loEmpleado.Sueldo)
Console.ReadLine()
End Sub
End Module
Public Class Empleado
Public psNombre As String
' variables de propiedad
Private miCategoria As Integer
Private mdbSueldo As Double
' procedimientos de propiedad
Public Property Categoria() As Integer
Get
Return miCategoria
End Get
Set(ByVal Value As Integer)
miCategoria = Value
End Set
End Property
Public Property Sueldo() As Double
Get
Return mdbSueldo
End Get
' cuando asignamos el valor a esta propiedad,
' ejecutamos código de validación en el bloque Set
Set(ByVal Value As Double)
' si la categoría del empleado es 1...
If miCategoria = 1 Then
' ...pero el sueldo supera 200
If Value > 200 Then
' mostrar un mensaje y asignar un cero
Console.WriteLine("La categoría no corresponde con el sueldo")
mdbSueldo = 0
Else
' si todo va bien, asignar el sueldo
mdbSueldo = Value
End If
End If
End Set
End Property
End Class
Código fuente 226

Encapsulación a través de propiedades - I

Una de las características de la OOP, la encapsulación, establece que el código de una clase debe permanecer, siempre que sea posible, protegido de modificaciones no controladas del exterior (código cliente). 
Nuestra clase debe actuar como una especie de caja negra, que expone un interfaz para su uso, pero que no debe permitir el acceso a la implementación de dicho interfaz. Supongamos que en nuestra clase Empleado necesitamos crear un elemento para guardar el sueldo pagado, pero el importe del sueldo deberá estar entre un rango de valores en función de la categoría del empleado. Si la categoría es 1, el sueldo estará entre 1 y 200, mientras que si la categoría es 2, el sueldo podrá llegar hasta 300. Si abordamos este problema utilizando campos de clase, puede ocurrir lo que mostramos en el Código fuente 225.

Module General
Sub Main()
Dim loEmpleado As Empleado
loEmpleado = New Empleado()
loEmpleado.psNombre = "Juan"
loEmpleado.piCategoria = 1
' atención, el sueldo para este empleado
' debería estar entre 1 a 200, debido a su categoría
loEmpleado.pdbSueldo = 250
End Sub
End Module
Public Class Empleado
Public msNombre As String
Public miCategoria As Integer
Public mdbSueldo As Double
End Class
Código fuente 225

Ventajas en el uso de propiedades

Comprobada la facilidad de los campos de clase, el lector se estará preguntando en estos momentos por qué debe utilizar propiedades, si en definitiva, su finalidad es la misma que los campos: guardar un valor en el objeto. Existen varias y poderosas razones, por las cuales nos debemos decantar en muchas ocasiones, hacia el uso de propiedades. En los siguientes apartados haremos una descripción de ellas.