Menu
Coddy logo textTech

IDisposable y la sentencia using

Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy — lección 45 de 70.

Algunos objetos retienen recursos que el recolector de basura no puede limpiar automáticamente: identificadores de archivos, conexiones a bases de datos, sockets de red. La interfaz IDisposable proporciona una forma estándar de liberar estos recursos cuando hayas terminado de usarlos.

Una clase implementa IDisposable proporcionando un método Dispose():

public class FileHandler : IDisposable
{
    private bool disposed = false;
    
    public void Dispose()
    {
        if (!disposed)
        {
            Console.WriteLine("Releasing resources...");
            disposed = true;
        }
    }
}

Podrías llamar a Dispose() manualmente, pero ¿qué pasa si ocurre una excepción antes de llegar a esa línea? La sentencia using garantiza que la limpieza ocurra, incluso cuando las cosas salen mal:

using (FileHandler handler = new FileHandler())
{
    // Trabajar con el manejador
}  // Dispose() se llama automáticamente aquí

El C# moderno ofrece una sintaxis más limpia con la declaración using, que libera el objeto cuando sale del alcance:

void ProcessFile()
{
    using FileHandler handler = new FileHandler();
    // Trabajar con el manejador
}  // Se llama a Dispose() cuando el método finaliza

La sentencia using funciona con cualquier tipo que implemente IDisposable. Clases integradas como StreamReader, SqlConnection y HttpClient implementan esta interfaz, lo que hace que la gestión adecuada de recursos sea sencilla y fiable.

challenge icon

Desafío

Fácil

Vamos a construir un simulador de conexión a base de datos que demuestre la gestión adecuada de recursos utilizando la interfaz IDisposable y la sentencia using. Crearás una clase que rastree cuándo se abren y cierran las conexiones, asegurando que los recursos siempre se liberen correctamente.

Organizarás tu código en dos archivos:

  • DatabaseConnection.cs: Crea una clase DatabaseConnection en el espacio de nombres DataAccess que implemente IDisposable. Tu conexión debe:
    • Tener una propiedad ConnectionName (string, de solo lectura) establecida a través del constructor
    • Rastrear si la conexión ha sido eliminada (disposed) utilizando un campo booleano privado
    • Imprimir Connection opened: {ConnectionName} cuando se crea el objeto (en el constructor)
    • Implementar el método Dispose() para imprimir Connection closed: {ConnectionName}, pero solo si no ha sido eliminada previamente (evitar la doble eliminación)
    • Tener un método Query(string sql) que devuelva Executing on {ConnectionName}: {sql}
  • Program.cs: En tu archivo principal, demuestra la gestión adecuada de recursos utilizando la sentencia using con tu conexión de base de datos. Crea una conexión, ejecuta una consulta y deja que el bloque using maneje la limpieza automáticamente.

Recibirás dos entradas:

  • El nombre de la conexión (por ejemplo, MainDB)
  • La consulta SQL a ejecutar (por ejemplo, SELECT * FROM Users)

Usa una sentencia using para crear tu conexión. Dentro del bloque, llama al método Query e imprime su resultado. Cuando el bloque using termine, se llamará a Dispose() automáticamente.

Por ejemplo, si las entradas son MainDB y SELECT * FROM Users, la salida debería ser:

Connection opened: MainDB
Executing on MainDB: SELECT * FROM Users
Connection closed: MainDB

Observa cómo la conexión se cierra automáticamente cuando termina el bloque using; nunca tienes que acordarte de llamar a Dispose() manualmente. ¡Este patrón asegura que los recursos siempre se limpien, incluso si ocurre una excepción dentro del bloque!

Hoja de referencia

La interfaz IDisposable proporciona una forma estándar de liberar recursos que el recolector de basura no puede limpiar automáticamente (identificadores de archivos, conexiones a bases de datos, sockets de red).

Una clase implementa IDisposable proporcionando un método Dispose():

public class FileHandler : IDisposable
{
    private bool disposed = false;
    
    public void Dispose()
    {
        if (!disposed)
        {
            Console.WriteLine("Releasing resources...");
            disposed = true;
        }
    }
}

La sentencia using garantiza que la limpieza ocurra automáticamente, incluso cuando se producen excepciones:

using (FileHandler handler = new FileHandler())
{
    // Work with the handler
}  // Dispose() called automatically here

El C# moderno ofrece una sintaxis más limpia con la declaración using:

void ProcessFile()
{
    using FileHandler handler = new FileHandler();
    // Work with handler
}  // Dispose() called when method exits

La sentencia using funciona con cualquier tipo que implemente IDisposable, incluyendo clases integradas como StreamReader, SqlConnection y HttpClient.

Pruébalo tú mismo

using System;
using DataAccess;

class Program
{
    public static void Main(string[] args)
    {
        string connectionName = Console.ReadLine();
        string sqlQuery = Console.ReadLine();
        
        // TODO: Usar una sentencia 'using' para crear una DatabaseConnection
        // Dentro del bloque using:
        // 1. Llamar al método Query con el sqlQuery
        // 2. Imprimir el resultado del método Query
        // El método Dispose se llamará automáticamente cuando termine el bloque using
        
    }
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos