Duración de los objetos: cómo se crean y destruyen
Actualización: noviembre 2007
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.
Sub New y Sub Finalize
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. A diferencia de Class_Initialize, el constructor Sub New sólo se puede ejecutar 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.
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, este método sólo debe definirse 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 hay un retardo entre el momento en que un objeto pierde el ámbito y el momento en que Visual Basic llama al destructor Finalize. Visual Basic 2005 y versiones posteriores proporcionan un segundo tipo de destructor, Dispose, al que se puede llamar explícitamente en cualquier momento para liberar recursos de forma inmediata.
Nota: |
---|
Un destructor Finalize no debería producir excepciones porque la aplicación no puede controlarlas y pueden hacer que la aplicación finalice. |
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 que administra activamente los recursos del sistema si implementan la interfaz IDisposable. IDisposable tiene un método, Dispose, al que los clientes deben llamar cuando terminan de utilizar 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.
Visualización
El diagrama siguiente muestra qué métodos se heredan y qué métodos se reemplazan en la clase derivada.
Cuando se sigue el modelo DisposeFinalize, los recursos de la clase derivada y de la clase base se desechan correctamente. El diagrama siguiente muestra a qué métodos se llaman cuando las clases se desechan y finalizan.
Recolección de elementos no utilizados y el destructor Finalize
.NET Framework utiliza el sistema de recolección de elementos no utilizados por seguimiento 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.
Vea también
Tareas
Cómo: Implementar el modelo DisposeFinalize (Visual Basic)
Conceptos
Inicialización y finalización de componentes
Métodos de finalización y destructores
Referencia
Utilizar constructores y destructores