Duración de los objetos: cómo se crean y destruyen (Visual Basic)
Una instancia de una clase (un objeto) se crea mediante la palabra clave New.A menudo hay que realizar tareas de inicialización en los objetos nuevos antes de utilizarlos.Entre las tareas de inicialización comunes se incluyen abrir archivos, conectar con bases de datos y leer valores de claves del Registro.Visual Basic controla la inicialización de objetos nuevos mediante unos procedimientos denominados constructores (métodos especiales que proporcionan control sobre la inicialización).
Common Language Runtime (CLR) libera un objeto cuando éste ha salido del ámbito.Visual Basic controla la liberación de recursos del sistema mediante unos procedimientos denominados destructores.Juntos, los constructores y destructores permiten la creación de bibliotecas de clases robustas y predecibles.
Utilizar constructores y destructores
Los constructores y destructores controlan la creación y destrucción de objetos.Los procedimientos Sub New y Sub Finalize de Visual Basic inicializan y destruyen objetos; reemplazan a los métodos Class_Initialize y Class_Terminate que se utilizaban en Visual Basic 6.0 y versiones anteriores.
Sub New
El constructor Sub New sólo puede ejecutarse una vez cuando se crea una clase.Sólo se le puede llamar explícitamente desde la primera línea de código de otro constructor en la misma clase o en una clase derivada.Además, el código del método Sub New siempre se ejecuta antes de cualquier otro código en una clase.Visual Basic 2005 y las versiones posteriores crean implícitamente en tiempo de ejecución un constructor Sub New si no define explícitamente un procedimiento Sub New para una clase.
Para crear un constructor para una clase, cree un procedimiento denominado Sub New en cualquier parte de la definición de clase.Para crear un constructor parametrizado, especifique los nombres y los tipos de datos de los argumentos de Sub New tal como lo haría en cualquier otro procedimiento, como en el código siguiente:
Sub New(ByVal s As String)
Con frecuencia, los constructores están sobrecargados, como en el código siguiente:
Sub New(ByVal s As String, i As Integer)
Cuando se define una clase derivada de otra, la primera línea del constructor debe ser una llamada al constructor de la clase base, a no ser que ésta tenga un constructor accesible que no requiera parámetros.Por ejemplo, para llamar a la clase base que contiene el constructor anterior, sería MyBase.New(s).En caso contrario, MyBase.New es opcional y el tiempo de ejecución de Visual Basic la llama implícitamente.
Después de escribir código para llamar al constructor del objeto primario, se puede agregar código de inicialización al procedimiento Sub New.Sub New puede aceptar argumentos cuando se llama como constructor parametrizado.Estos parámetros se pasan desde el procedimiento que llama al constructor; por ejemplo, Dim AnObject As New ThisClass(X).
Sub Finalize
Antes de liberar objetos, CLR llama automáticamente al método Finalize para los objetos que definen un procedimiento Sub Finalize.El método Finalize puede contener código que es necesario ejecutar inmediatamente antes de destruir un objeto, por ejemplo el código para cerrar archivos y guardar información de estado.Puesto que se produce una ligera disminución del rendimiento al ejecutar Sub Finalize, sólo debe definirse un método Sub Finalize cuando sea necesario liberar objetos explícitamente.
[!NOTA]
El recolector de elementos no utilizados de CLR no se deshace de los objetos no administrados (ni puede eliminarlos), que son aquellos que el sistema operativo ejecuta directamente, fuera del entorno de CLR.Esto se debe a que hay que deshacerse de los objetos no administrados diferentes de maneras distintas.Esa información no está asociada directamente al objeto no administrado; se debe buscar en la documentación del objeto.Una clase que utiliza objetos no administrados debe deshacerse de ellos en su método Finalize.
El destructor Finalize es un método protegido al que sólo se puede llamar desde la clase a la que pertenece o desde clases derivadas.El sistema llama automáticamente a Finalize cuando se destruye un objeto, por lo que no se debe llamar explícitamente a Finalize desde fuera de una implementación de su clase derivada.
A diferencia de Class_Terminate, que se ejecuta en cuanto un objeto se establece en nothing, normalmente se suele producir un retraso desde que un objeto pierde el ámbito hasta que Visual Basic llama al destructor Finalize.Visual Basic 2005 y versiones posteriores permiten un segundo tipo de destructor, Dispose, al que se puede llamar explícitamente en cualquier momento para liberar recursos inmediatamente.
[!NOTA]
Un destructor Finalize no debería producir excepciones porque la aplicación no puede controlarlas y pueden hacer que la aplicación finalice.
Cómo funcionan los métodos New y Finalize en una jerarquía de clases
Siempre que se crea una instancia de una clase, Common Language Runtime (CLR) intenta ejecutar un procedimiento denominado New, si existe en ese objeto.New es un tipo de procedimiento denominado constructor que se usa para inicializar los objetos nuevos antes de que se ejecute cualquier otro código en un objeto.Un constructor New puede utilizarse para abrir archivos, conectarse a bases de datos, inicializar variables y realizar cualquier otra tarea necesaria antes de poder utilizar un objeto.
Cuando se crea una instancia de una clase derivada, se ejecuta primero el constructor Sub New de la clase base, seguido de los constructores de las clases derivadas.Esto se debe a que la primera línea de código en un constructor Sub New utiliza la sintaxis MyBase.New() para llamar al constructor de la clase situada inmediatamente por encima en la jerarquía de clases.Después se llama al constructor Sub New para cada clase de la jerarquía hasta llegar al constructor de la clase base.En este punto, se ejecuta el código del constructor para la clase base, seguido del código en cada constructor de todas las clases derivadas, ejecutándose en último lugar el código de las clases más derivadas.
Cuando un objeto deja de ser necesario, CLR llama al método Finalize para ese objeto antes de liberar su memoria.El método Finalize se denomina destructor porque realiza tareas de limpieza, como guardar información de estado, cerrar archivos y conexiones a bases de datos, y otras tareas que deben realizarse antes de liberar el objeto.
Interfaz IDisposable
A menudo, las instancias de clase controlan los recursos que no administra CLR, como identificadores de Windows y conexiones de bases de datos.Estos recursos se deben desechar en el método Finalize de la clase, a fin de liberarlos cuando el recolector de elementos no utilizados destruya el objeto.Sin embargo, el recolector de elementos no utilizados sólo destruye los objetos cuando CLR requiere más memoria libre.Esto significa que los recursos pueden no liberarse hasta mucho después de que el objeto salga del ámbito.
Para complementar la recolección de elementos no utilizados, las clases pueden proporcionar un mecanismo para administrar los recursos del sistema activamente si implementan la interfaz IDisposable.IDisposable tiene un método, Dispose, al que deberían llamar los clientes cuando terminan de usar un objeto.Se puede utilizar el método Dispose para liberar recursos inmediatamente y realizar tareas como cerrar archivos y conexiones de bases de datos.A diferencia del destructor Finalize, el método Dispose no se llama automáticamente.Los clientes de una clase deben llamar explícitamente a Dispose cuando se desea liberar recursos inmediatamente.
Implementar IDisposable
Una clase que implementa la interfaz IDisposable debería incluir estas secciones de código:
Un campo para mantener el seguimiento de si se ha desechado el objeto:
Protected disposed As Boolean = False
Una sobrecarga de Dispose que libera los recursos de la clase.Los métodos Dispose y Finalize de la clase base deben llamar a este método:
Protected Overridable Sub Dispose(ByVal disposing As Boolean) If Not Me.disposed Then If disposing Then ' Insert code to free managed resources. End If ' Insert code to free unmanaged resources. End If Me.disposed = True End Sub
Una implementación de Dispose que contiene sólo el código siguiente:
Public Sub Dispose() Implements IDisposable.Dispose Dispose(True) GC.SuppressFinalize(Me) End Sub
Un reemplazo del método Finalize que contiene sólo el código siguiente:
Protected Overrides Sub Finalize() Dispose(False) MyBase.Finalize() End Sub
Derivar de una clase que implementa IDisposable
Una clase derivada de una clase base que implementa la interfaz IDisposable no necesita reemplazar ninguno de los métodos base a menos que utilice recursos adicionales que se deban desechar.En esa situación, la clase derivada debería reemplazar el método Dispose(disposing) de la clase base para desechar los recursos de la clase derivada.Este reemplazo debe llamar al método Dispose(disposing) de la clase base.
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposed Then
If disposing Then
' Insert code to free managed resources.
End If
' Insert code to free unmanaged resources.
End If
MyBase.Dispose(disposing)
End Sub
Una clase derivada no debería reemplazar los métodos Dispose y Finalize de la clase base.Cuando se llama a estos métodos desde una instancia de una clase derivada, la implementación de estos métodos en la clase base llama al reemplazo del método Dispose(disposing) en la clase derivada.
Recolección de elementos no utilizados y el destructor Finalize
.NET Framework utiliza el sistema de recolección de elementos no utilizados por traza de referencias para liberar periódicamente los recursos no utilizados.Visual Basic 6.0 y las versiones anteriores utilizaban un sistema diferente denominado recuento de referencias para administrar los recursos.Aunque ambos sistemas realizan la misma función automáticamente, existen algunas diferencias importantes.
CLR destruye periódicamente objetos cuando el sistema determina que ya no son necesarios.Los objetos se liberan con mayor rapidez cuando los recursos del sistema son escasos y con menor frecuencia en caso contrario.El retraso que se produce entre el momento en que un objeto pierde su ámbito y el momento en que CLR lo libera significa que, a diferencia de lo que ocurre con los objetos en Visual Basic 6.0 y versiones anteriores, no se puede determinar exactamente cuándo se destruirá el objeto.En tal situación, se dice que los objetos tienen duración no determinista.En la mayoría de los casos, la duración no determinista no cambia la forma de escribir las aplicaciones, siempre que se recuerde que el destructor Finalize puede no ejecutarse inmediatamente cuando un objeto pierde su ámbito.
Otra diferencia entre los sistemas de recolección de elementos no utilizados tiene que ver con el uso de Nothing.Para aprovechar el recuento de referencias de Visual Basic 6.0 y versiones anteriores, los programadores a veces asignaban Nothing a variables de objeto para liberar las referencias que esas variables almacenaban.Si la variable almacenaba la última referencia al objeto, los recursos del objeto se liberaban inmediatamente.En las versiones posteriores de Visual Basic, aunque puede haber casos en los que este procedimiento sea aún útil, su realización nunca ocasionará que el objeto de referencia libere sus recursos inmediatamente.Para liberar inmediatamente los recursos, utilice el método Dispose del objeto, si está disponible.La única vez en que debe establecerse una variable en Nothing es cuando su duración sea larga con relación al tiempo que necesita el recolector de elementos no utilizados para detectar objetos huérfanos.