Uso de IDisposable.

Imaginemos un código tal que así:

Stream stream = storageContainer.CreateFile("MyFile.txt");
StreamWriter writer = new StreamWriter(stream);
writer.Write("Stuff");
writer.Close();
stream.Close();

¿Os duele la vista igual que a mi?

La interfaz IDisposable que permite a los objetos liberar los recursos que tengan correctamente y de forma determinista. Si en el código anterior se produjese una excepción se corre el riesgo de no liberar los recursos. Si acaso, en el mejor de los casos, esos recursos se liberarían cuando pase el recolector de basura y en el peor de los casos nunca se liberarían.

Ahora, muchos dicen: "Pero en ese código pasa tan poca cosa, ¿qué podría provocar una excepción?". Y tienen razón, para el código anterior. pero por ejemplo en los juegos se hace mucho más que eso. Cosas como serializar (o deserializar) objetos, parsear strings a números, y un montón de cosas más. Hay un montón de sitios en los que las operaciones de manipulación de Streams pueden lanzar excepciones, así que es mejor hacer estas cosas bien.

Cada vez que un tipo implemente IDisposable debe de tomarse como sugerencia usar la sentencia "using". Y es que esta sentencia no sólo sirve para incluir namespaces, sino para marcar el ámbito de los objetos. Si además la clase implementa IDisposable, nos aseguramos de que al final se llamará a la función Dispose. Así que vamos a reescribir el código de la siguiente forma:

using (Stream stream = storageContainer.CreateFile("MyFile.txt"))
{
    using (StreamWriter writer = new StreamWriter(stream))
    {
        writer.Write("Stuff");
    }
}

Esto no sólo nos ayuda a marcar el ámbito de nuestros objetos sino que también si el código lanza alguna excepción, también se llama al Dispose. Básicamente, el compilador traduce este código en:

{
    Stream stream = storageContainer.CreateFile("MyFile.txt");
    try {
        StreamWriter writer = new StreamWriter(stream);

        try {
            writer.Write("Stuff");
        }
        finally {
            if (writer != null)
                ((IDisposable)writer).Dispose();
        }
    }
    finally {
        if (stream != null)
            ((IDisposable)stream).Dispose();
    }
}

Como podemos sugerir de este código, si ocurre una excepción se asegura de que se llame al Dispose, pero la excepción sigue lanzándose en la pila.

Para ver un ejemplo más claro descargaos Ejemplo DisposableObjects y podéis verlo con más claridad. Es un proyecto de Windows Forms con dos clases:

  • ConstructorException: Que en el constructor lanza una excepción, y al hacer un using no se ejecuta el método Dispose().
  • ConstructorSinExcepcion: Es exactamente igual que ConstructorException pero sin lanzar una excepción y vemos cómo se ejecuta el método Dispose().

Captura de pantalla de DisposableObjects

Si vais a implementar clases IDisposables tened en cuenta también cómo implementar el método Dispose, que da para otro post.

Espero que sirva.

Juan María Laó Ramos.

2 pensamientos en “Uso de IDisposable.

Deja un comentario