[.NET] Mis interfaces favoritos: IDisposable


Siguiendo con la serie de Mis interfaces favoritos, en esta entrega conoceremos a nuestro amado interfaz IDisposable.
Como ya dijimos en post anteriores, .NET usa dos zonas de memoria llamadas Heap y Stack.

  • En el Stack se almacenan los Value Types, que son básicamente los tipos básicos definidos por el Framework. Esta zona de memoria generalmente la gestiona el propio sistema operativo.
  • En el Heap se almacenan los datos reales de los Reference Types, zona de memoria que gestiona automáticamente el motor en tiempo de ejecución de .NET, el Common Language Runtime (CLR). Esta gestión de memoria automática se denomina Garbage Collection y el “problema” que tenemos es que no es posible controlar la liberación y recuperación de los objetos en el Heap.

El Garbage Collector (GC) únicamente controla la memoria memoria ocupada por los objetos de .NET dejando la responsabilidad al programador la gestión de los recursos no manejados. Aunque existen métodos para forzar la liberación de objetos no referenciados, no se garantiza que se eliminen del todos dichos objetos de la memoria, por lo que es muy ineficiente el realizar esto en nuestros programas.
El CLR provee una forma de implementar la finalización de los objetos de forma determinista a través del interfaz IDisposable, usando un método Dispose  para una liberación de recursos explícita y en algunos casos usar un método Finalize para una liberación implícita.
Esto se conoce como Dispose Pattern (o el IDisposable Pattern).

¿Qué es un Finalizer?

Los Finalizer son como destructores que son llamados cuando el objeto sale fuera del ámbito del método o bloque en el que se usa.
Normalmente no es necesario implementarlos pero si queremos usar el IDisposable pattern y a la vez queremos realizar la liberación de recursos de nuestros objetos locales cuando salen fuera de su ámbito es necesario implementar un finalizador en nuestra clase.

La forma de implementarlo es la siguiente:

class MiClase
{
   ~MiClase()
   {
       // esto es un Finalizer
   }
}

¿Cómo implementamos el IDisposable Pattern?

El interfaz IDisposable se define de la siguiente forma

public interface IDisposable
{
   void Dispose();
}

La forma correcta de implementar este patrón es tomando el siguiente ejemplo:

public class MiClase : IDisposable
{
   private bool disposed;

   public void Dispose()
   {
      Dispose(true);
      // como ya hemos liberado los recursos,
      // se le informa al GC de que no llame al Finalizer de la clase
      GC.SuppressFinalize(this);
   }

   ~MiClase()
   {
      Dispose(false);
   }

   protected virtual void Dispose(bool disposing)
   {
      if (!disposed)
      {
         if (disposing)
         {
            // liberar objetos manejados que implementen el interfaz IDisposable
         }

         // liberar objetos no manejados
         // estableciendo las referencias a null
         disposed = true;
      }
   }
}

Tenemos que tener en cuenta si implementamos una clase que se deriva de otra que implementa IDisposable, en llamar al método Dispose(bool) de la clase base cuando llamamos a Dispose().

Un ejemplo de esto que comento es lo siguiente, como se ve, NO se reimplementa Dispose y Finalizer en la clase derivada:

public class MiClaseBase : IDisposable
{
    private bool disposed = false; 

    public MiClaseBase()
    {
        // instanciar recursos
    }

    public void Dispose()
    {
       Dispose(true);
       // como ya hemos liberado los recursos,
       // se le informa al GC de que no llame al Finalizer de la clase
       GC.SuppressFinalize(this);
    }

    ~MiClaseBase()
    {
        Dispose(false);
    }  

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // liberar objetos manejados que implementen el interfaz IDisposable
            }

            // liberar objetos no manejados
            // estableciendo las referencias a null
            disposed = true;
        }
    }
}

public class MiClaseDerivada : MiClaseBase
{

    private bool disposed = false;  

    public MiClaseDerivada() : base()
    {
        // instanciar más recursos
    }

    protected override void Dispose(bool disposing)
    {
       if (!disposed)
       {
            if (disposing)
            {
                // liberar objetos manejados que implementen el interfaz IDisposable
            }

            // liberar objetos no manejados
            // estableciendo las referencias a null
            disposed = true;
        }

        // como es una clase derivada, realizar la llamada
        // al método Dispose de la clase base
        base.Dispose(disposing);
    }
}

Empleando el bloque using{}

Como no podemos obligar al programador que usa nuestra clase que implementa IDisposable a que llame directamente al método Dispose() de la misma, C# nos provee de un bloque sintáctico denominado using statement. Ese bloque de código garantiza el uso correcto de objetos IDisposable.

“Como norma, cuando utilice un objeto IDisposable, declárelo y cree instancias del mismo en una instrucción using. La instrucción using llama al método Dispose en el objeto de la forma correcta.”

Como ejemplo de uso se muestra el siguiente código:

public void MiMetodo()
{
    using (MiClase miClase = new MiClase())
    {
        MiClase.Metodo1();
    }
}

Este trozo de código al realizar la compilación se traduce en:

public void MiMetodo()
{
   MiClase  miClase = new MiClase();

   try
   {
      miClase.Metodo1();
   }
   finally
   {
      if (miClase != null)
      {
         IDisposable disposable = miClase ;
         disposable.Dispose();
      }
   }
}

Como se puede observar, el uso del bloque using deja un código más limpio y sencillo.

Hemos aprendido a implementar correctamente el IDisposable Pattern y a emplear el bloque using para asegurarnos de llamar a Dispose() cuando se sale fuera del ámbito del bloque.

Para más información sobre este interfaz podéis visitar los siguientes enlaces:

Implementing a Dispose Method

Anuncios

4 comentarios en “[.NET] Mis interfaces favoritos: IDisposable

  1. Buenas!
    Solo un (pequeño y probablemente irrelevante) comentario:
    Los ValueTypes *pueden* almacenarse en la Stack, pero no tienen por qué. Es decir, el estándard de .NET no define obligatoriamente que un ValueType deba estar en el Stack.
    Es cierto que en la implementación de .NET de Microsoft, los ValueTypes se almacenan habitualmente en el Stack, pero hay algunos casos concretos en que esto NO es así.

    Al respecto de esto es interesante (como siempre) un post de Eric Lippert: https://blogs.msdn.microsoft.com/ericlippert/2010/09/30/the-truth-about-value-types/

    Saludos!

    Le gusta a 1 persona

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s