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 finalizaLa 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.
Desafío
FácilVamos 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 claseDatabaseConnectionen el espacio de nombresDataAccessque implementeIDisposable. 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 imprimirConnection closed: {ConnectionName}, pero solo si no ha sido eliminada previamente (evitar la doble eliminación) - Tener un método
Query(string sql)que devuelvaExecuting on {ConnectionName}: {sql}
- Tener una propiedad
Program.cs: En tu archivo principal, demuestra la gestión adecuada de recursos utilizando la sentenciausingcon tu conexión de base de datos. Crea una conexión, ejecuta una consulta y deja que el bloqueusingmaneje 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: MainDBObserva 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 hereEl 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 exitsLa 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
}
}
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
1Fundamentos de OOP
Archivos externosNamespaces y directivasIntro a clases y objetosLa palabra clave 'this'Métodos y parámetrosCampos vs PropiedadesConstructoresInicializadores de objetosResumen - Calculadora simple4Herencia
Sintaxis básica de herencia (:)La palabra clave 'base'Palabras clave Virtual y OverrideClases selladas (Sealed)La clase base 'object'Resumen: Jerarquía de empleados7Características avanzadas
Sobrecarga de operadoresIndexadores (this[])Sobrescritura de ToString()Métodos de extensiónResumen - Lista personalizada10Patrones de diseño - Parte 1
Introducción a los patrones de diseñoSingleton Thread-SafePatrón FactoryPatrón Observer (Eventos)Patrón Strategy2Propiedades y miembros estáticos
Propiedades autoimplementadasPropiedades de solo lectura y escrituraCampos y métodos estáticosClases estáticasMiembros con cuerpo de expresión5Polimorfismo e Interfaces
Polimorfismo: Compilación vs. EjecuciónInterfaz vs. Clase AbstractaInterfaces MúltiplesInterfaces ExplícitasUpcasting y DowncastingRepaso: Calculadora de Figuras8Conceptos avanzados de POO
Composición sobre herenciaGenéricos (clases y métodos)Delegados y eventosAtributos y ReflexiónIDisposable y la sentencia usingFundamentos de Inyección de dependencias11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Arquitectura de clases
Datos de instancia vs. estáticosPalabras clave 'readonly' y 'const'Campos de respaldoResumen - Gestor de cuentas bancarias6Encapsulamiento
Modificadores de accesoPropiedades para el encapsulamientoImplementación de ocultamiento de datosPatrones de inmutabilidadRepaso - Registros de estudiantes