C# - IDisposable

Descripción general

Para destruir un objeto de forma ‘educada’ Microsoft indica que se use la interfaz IDisposable. Usando el método Dispose. No voy a explicar aquí todo el asunto del recolector de basura, porque se supone algo sabido

[TOC] Tabla de Contenidos


↑↑↑

C# - IDisposable


↑↑↑

Destructores

En C# también hay que implementar un método especial llamado destructor. Un destructor es un método especial, parecido a un constructor, salvo porque el motor en tiempo de ejecución lo llama después de que haya desaparecido la última referencia a un objeto. Por esa razón tiene que tener un modificador de acceso de Private porque NADIE puede llamarlo explícitamente

La sintaxis de un constructor es una tilde ( ~ se obtiene con [Alt + 126]) seguida del nombre de la clase.


↑↑↑

Ejemplo


class Ejemplo:IDisposable
 {
     /// <summary>
     ///   El constructor del objeto
     /// </summary>
     public Ejemplo() {
     // Este es el constructor
     }
 
     /// <summary>
     ///  Destructor del objeto
     ///  Nota, el carácter [ ~ ] se obtiene con 
     ///  inicio>Todos los programas > accesorios> 
     ///  Herramientas del sistema > Mapas de caracteres
     ///  O Bien pulsando [Alt + 126] en el teclado numérico
     /// </summary>
     private ~Ejemplo(){
        // este es el destructor
        Dispose(); 
     }
 }
 
 

¿Qué debemos hacer cuando escribimos una clase, escribir un destructor o implementar la interfaz IDisposable? La pregunta que en realidad hay que hacerse es como escribir el proceso para asegurarnos de que el objeto se elimina totalmente y además solo una vez para evitar problemas a llamadas a objetos que ya son nulos

Sabemos que el recolector de basura llama siempre a los destructores de las clases, pero no podemos saber cuándo se realizara la llamada. Es un problema derivado de cómo funciona el recolector de basura. Dicho de otro modo, podemos pensar y actuar como si el objeto estuviera destruido cuando en realidad, el recolector de basura no lo ha eliminado

Sabemos que si escribimos el método Dispose, podemos llamarlo en cualquier momento para destruir el objeto, pero hay dos problemas, uno que el programador se acuerde de hacerlo, y dos hay que evitar intentar destruir el objeto dos veces, porque cuando se ejecute destructor del objeto, el objeto en si ya no existe esta destruido.

Esto parece una tontería pero exige pero exige escribir un código Dispose con un poco de cuidado


↑↑↑

Implementación interfaz [IDisposable.Dispose]

Observa el ejemplo siguiente, la clase Persona (jolines que original con el nombre)

El código de la "Implementación interfaz [IDisposable]" se ha dividido en dos partes, en la primera hay tres funciones (y un aviso importante) que hay que adaptar para cada clase que escribamos, en la segunda parte, está el proceso que es invariable, y que no se modifica


class Persona : IDisposable
{
 
    /// <summary>
    ///   El constructor del objeto
    /// </summary>
    public Persona()
    {
        // Este es el constructor
    }
 
 
 
    // ====================================================
    #region "Implementación interfaz [IDisposable.Dispose]"
 
 
    // ====================================================
    // Interfaz [IDisposable.Dispose]
    // Las funciones siguientes son las que hay que modificar para cada clase 
    // el resto de la implementación no hay que tocarlo
    // ====================================================
 
    /// <summary>
    ///  Destructor del objeto
    ///  Nota, el carácter [ ~ ] se obtiene con 
    ///  Inicio > Todos los programas > accesorios> 
    ///  Herramientas del sistema > Mapas de caracteres
    ///  O Bien pulsando [Alt + 126] en el teclado numérico
    /// </summary>
    private ~Persona()
    {
        // Escenario en el que el sistema llama al método [Dispose]
        this.Dispose(false);
    }
        
    /// <summary>
    ///   Eliminar recursos SI administrados
    /// </summary>
    private void EliminaObjetosAdministradosDispose()
    {
        /*
         * -------------------------------
         * Aquí el proceso de eliminar recursos SI administrados
         * -------------------------------
         * Eliminar los recursos administrados.  
         * Es decir objetos normales de C# .NET
         * También las variables de cadena, los arrays y 
         * los delegados, se ponen a NULL
         * -------------------------------
         */

        // (Por ejemplo, desacoplar funciones y delegados)

        // (Por ejemplo, Cerrar bases de datos)
        // if (myConnection != null)
        // {
        //    myConnection.Dispose();
        //    myConnection = null;
        // }

        // (Por ejemplo, eliminar objetos .NET contenidos)
        // if (arbol == null){
        //     arbol.dispose();
        //     arbol = null;
        // }
 
        // (Por ejemplo), Una variable muy grande
    }
 
 
        /// <summary>
        ///   Eliminar recursos NO administrados
        /// </summary>
        private void EliminaObjetosNOAdministradosDispose()
        {
            // Si [disposing] es igual a false, 
            // Sólo los recursos no administrados pueden ser eliminados.
            // OBSERVACION: Recursos no administrados; son Librerías de
            // Terceros, o por ejemplo usar el Word o Excel desde programa
            // -------------------------------
            // Aquí el proceso de eliminar recursos NO administrados

            // Si existen 
        }



        // ------------------------------
        //   **** ATENCION  *****
        // Si es una clase derivada hay que llamar al método dispose de la clase Base
        // en la función [protected virtual void Dispose(bool disposing)]
        // en el apartado [finally] esta comentado el código que se tiene que ejecutar
        // solo hay que quitar los comentarios
        // ------------------------------

 
 
    // ====================================================
    // Interfaz [IDisposable.Dispose]
    // El código que sigue a continuación no hay que tocarlo
    // ====================================================
 
    /// <summary>
    ///  Variable privada que se emplea pasa saber si el método [Dispose]
    ///  se ha ejecutado [Valor=True] o no [Valor=False]
    /// </summary>
    private bool myDisposeEjecutado = false;
 
    /// <summary>
    ///  El método [Dispose] de este objeto se ha ejecutado [True] o no [False]
    /// </summary>
    public bool IsDisposed
    {
        get
        {
            return myDisposeEjecutado;
        }
    }
        
    /// <summary>
    ///    Implementa la interfaz EXPLICITA [IDisposable] 
    ///</summary>
    void IDisposable.Dispose()
    {
        // Escenario en el que el usuario llama al método [Dispose]
        this.Dispose(true);
    }
 
    /// <summary>
    ///    Implementa la interfaz IDisposable.
    /// </summary>
    /// <remarks>
    ///    <para>No hacer este método virtual. </para>
    ///    <para>Una clase derivada no debe ser capaz de reemplazar este método.</para>
    /// </remarks>
    public void Dispose()
    {
        // Escenario en el que el usuario llama al método [Dispose]
        this.Dispose(true);
    }
        
    //---------------------------------------
    /// <summary>Implementa IDisposable.</summary>
    /// <remarks>
    ///    <para>Dispose (bool disposing) se ejecuta en dos escenarios distintos.</para>
    ///    <para>A) Si [disposing] es igual a true, el método ha sido llamado directamente</para>
    ///    <para>    O indirectamente por el código de un usuario. </para>
    ///    <para>    Los recursos administrados y no administrados se pueden eliminar.</para>
    ///    <para> B) Si [disposing] es igual a false, el método ha sido llamado por el</para>
    ///    <para>    [runtime] desde el interior del finalizador y usted no debe hacer</para> 
    ///    <para>    referencia a otros objetos, ni siquiera para eliminarlos.</para>
    ///    <para>    Sólo los recursos no administrados pueden ser eliminados. </para>
    ///    <para> C) en cualquier caso solo se ejecuta una vez</para>
    /// </remarks>
    protected virtual void Dispose(bool disposing)
    {
        // Comprueba si este método ya ha sido llamado.  
        // y ejecutado. Solo se ejecuta una vez y solo una
        if (!this.myDisposeEjecutado)
        {
            //---------------------------------------
            // Cerrar la puerta. Este método solo se ejecuta una vez
            // Para evitar problemas esta acción se realiza en [finally]
            // Para asegurarme de que SIEMPRE se cierra la puerta
            // myDisposeEjecutado = true;
            //---------------------------------------
            try
            {
                // -------------------------------
                // Si [disposing] es igual a true 
                // Los recursos administrados y no administrados se pueden eliminar. 
                // -------------------------------
                if (disposing)
                {
                    // -------------------------------
                    // Aquí el proceso de eliminar recursos SI administrados
                    // Es decir objetos normales de C# .NET
                    // -------------------------------
                    EliminaObjetosAdministradosDispose();
                }
                // -------------------------------
                // Si [disposing] es igual a false, 
                // Sólo los recursos no administrados pueden ser eliminados.
                // OBSERVACION: Recursos no administrados; son Librerías de
                // Terceros, o por ejemplo usar el Word o Excel desde programa
                // -------------------------------
                // Aquí el proceso de eliminar recursos NO administrados
                // -------------------------------
                EliminaObjetosNOAdministradosDispose();
            }
 
            finally
            {
                // -------------------------------
                // Cerrar la puerta, solo se pasa una vez
                myDisposeEjecutado = true;
 
                // -------------------------------
                // Llamar al método [Dispose] de la clase Base
                // Solo para las clases que heredan de una clase base
                // base.Dispose();
                // -------------------------------
 
                // Decir al recolector que no llame al destructor de este objeto
                // Porque este objeto ya esta [finalizado]
                GC.SuppressFinalize(this);
            }
        }
    }
 
    #endregion   // EOF "Implementacion interfaz [Dispose]"
    // ====================================================
 
}


↑↑↑

A.2.Enlaces

[Para saber mas]
[Grupo de documentos]
[Documento Index]
[Bloque de apuntes tácticos de C#]
[Documento Start]
[Imprimir el Documento]