Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
El recolector de elementos no utilizados (GC) de Common Language Runtime reclama la memoria usada por los objetos administrados. Normalmente, los tipos que usan recursos no administrados implementan la IDisposable interfaz o IAsyncDisposable para permitir que se recuperen los recursos no administrados. Cuando termine de usar un objeto que implemente IDisposable, llame a la implementación Dispose o DisposeAsync del objeto para realizar explícitamente la limpieza. Puede hacerlo de una de las maneras siguientes:
- Mediante la instrucción o declaración
using
de C# (Using
en Visual Basic). - Mediante la implementación de un bloque
try/finally
y una llamada al método Dispose o DisposeAsync enfinally
.
Importante
El GC no elimina los objetos, ya que no tiene conocimiento de IDisposable.Dispose() ni de IAsyncDisposable.DisposeAsync(). El GC solo sabe si un objeto es finalizable (es decir, define un Object.Finalize() método) y cuándo se debe llamar al finalizador del objeto. Para obtener más información, consulte Cómo funciona la finalización. Para obtener más información sobre la implementación Dispose
y DisposeAsync
, consulte:
Los objetos que implementan System.IDisposable o System.IAsyncDisposable siempre deben eliminarse correctamente, independientemente del ámbito de las variables, a menos que se indique explícitamente lo contrario. Los tipos que definen un finalizador para liberar recursos no administrados suelen llamar a GC.SuppressFinalize desde su implementación de Dispose
o DisposeAsync
. Llamar a SuppressFinalize indica al GC que el finalizador ya se ha ejecutado y que el objeto no debe ser promovido para su finalización.
La instrucción using
La using
instrucción en C# y la Using
instrucción de Visual Basic simplifican el código que debe escribir para limpiar un objeto. La using
instrucción obtiene uno o varios recursos, ejecuta las instrucciones que especifique y elimina automáticamente el objeto . Sin embargo, la using
instrucción solo es útil para los objetos que se usan dentro del ámbito del método en el que se construyen.
En el ejemplo siguiente se usa la using
instrucción para crear y liberar un System.IO.StreamReader objeto .
using System.IO;
class UsingStatement
{
static void Main()
{
var buffer = new char[50];
using (StreamReader streamReader = new("file1.txt"))
{
int charsRead = 0;
while (streamReader.Peek() != -1)
{
charsRead = streamReader.Read(buffer, 0, buffer.Length);
//
// Process characters read.
//
}
}
}
}
Imports System.IO
Module UsingStatement
Public Sub Main()
Dim buffer(49) As Char
Using streamReader As New StreamReader("File1.txt")
Dim charsRead As Integer
Do While streamReader.Peek() <> -1
charsRead = streamReader.Read(buffer, 0, buffer.Length)
'
' Process characters read.
'
Loop
End Using
End Sub
End Module
Una using
declaración es una sintaxis alternativa disponible donde se quitan las llaves, y cuyo ámbito es implícito.
using System.IO;
class UsingDeclaration
{
static void Main()
{
var buffer = new char[50];
using StreamReader streamReader = new("file1.txt");
int charsRead = 0;
while (streamReader.Peek() != -1)
{
charsRead = streamReader.Read(buffer, 0, buffer.Length);
//
// Process characters read.
//
}
}
}
Aunque la StreamReader clase implementa la IDisposable interfaz , que indica que usa un recurso no administrado, el ejemplo no llama explícitamente al StreamReader.Dispose método . Cuando el compilador de C# o Visual Basic encuentra la using
instrucción , emite lenguaje intermedio (IL) equivalente al código siguiente que contiene explícitamente un try/finally
bloque.
using System.IO;
class TryFinallyGenerated
{
static void Main()
{
var buffer = new char[50];
StreamReader? streamReader = null;
try
{
streamReader = new StreamReader("file1.txt");
int charsRead = 0;
while (streamReader.Peek() != -1)
{
charsRead = streamReader.Read(buffer, 0, buffer.Length);
//
// Process characters read.
//
}
}
finally
{
// If non-null, call the object's Dispose method.
streamReader?.Dispose();
}
}
}
Imports System.IO
Module TryFinallyGenerated
Public Sub Main()
Dim buffer(49) As Char
Dim streamReader As New StreamReader("File1.txt")
Try
Dim charsRead As Integer
Do While streamReader.Peek() <> -1
charsRead = streamReader.Read(buffer, 0, buffer.Length)
'
' Process characters read.
'
Loop
Finally
If streamReader IsNot Nothing Then DirectCast(streamReader, IDisposable).Dispose()
End Try
End Sub
End Module
La instrucción C# using
también permite adquirir varios recursos en una sola instrucción, que es internamente equivalente a instrucciones anidadas using
. En el ejemplo siguiente se crean instancias de dos StreamReader objetos para leer el contenido de dos archivos diferentes.
using System.IO;
class SingleStatementMultiple
{
static void Main()
{
var buffer1 = new char[50];
var buffer2 = new char[50];
using StreamReader version1 = new("file1.txt"),
version2 = new("file2.txt");
int charsRead1, charsRead2 = 0;
while (version1.Peek() != -1 && version2.Peek() != -1)
{
charsRead1 = version1.Read(buffer1, 0, buffer1.Length);
charsRead2 = version2.Read(buffer2, 0, buffer2.Length);
//
// Process characters read.
//
}
}
}
Bloque try/finally
En lugar de ajustar un bloque try/finally
en una instrucción using
, puede elegir implementar el bloque try/finally
directamente. Puede ser su estilo de codificación personal, o puede que quiera hacerlo por una de las siguientes razones:
- Para incluir un bloque
catch
que maneje las excepciones lanzadas en el bloquetry
. De lo contrario, las excepciones iniciadas en la instrucciónusing
no se controlan. - Para crear una instancia de un objeto que implementa IDisposable y cuyo ámbito no es local dentro del bloque en el que se declara.
El ejemplo siguiente es similar al ejemplo anterior, excepto que usa un try/catch/finally
bloque para crear instancias, usar y eliminar de un StreamReader objeto, y para controlar las excepciones producidas por el StreamReader constructor y su ReadToEnd método. El código en el bloque finally
verifica que el objeto que implementa IDisposable no sea null
antes de llamar al método Dispose. Si no se hace esto, se puede producir una NullReferenceException excepción en tiempo de ejecución.
using System;
using System.Globalization;
using System.IO;
class TryExplicitCatchFinally
{
static void Main()
{
StreamReader? streamReader = null;
try
{
streamReader = new StreamReader("file1.txt");
string contents = streamReader.ReadToEnd();
var info = new StringInfo(contents);
Console.WriteLine($"The file has {info.LengthInTextElements} text elements.");
}
catch (FileNotFoundException)
{
Console.WriteLine("The file cannot be found.");
}
catch (IOException)
{
Console.WriteLine("An I/O error has occurred.");
}
catch (OutOfMemoryException)
{
Console.WriteLine("There is insufficient memory to read the file.");
}
finally
{
streamReader?.Dispose();
}
}
}
Imports System.Globalization
Imports System.IO
Module TryExplicitCatchFinally
Sub Main()
Dim streamReader As StreamReader = Nothing
Try
streamReader = New StreamReader("file1.txt")
Dim contents As String = streamReader.ReadToEnd()
Dim info As StringInfo = New StringInfo(contents)
Console.WriteLine($"The file has {info.LengthInTextElements} text elements.")
Catch e As FileNotFoundException
Console.WriteLine("The file cannot be found.")
Catch e As IOException
Console.WriteLine("An I/O error has occurred.")
Catch e As OutOfMemoryException
Console.WriteLine("There is insufficient memory to read the file.")
Finally
If streamReader IsNot Nothing Then streamReader.Dispose()
End Try
End Sub
End Module
Puede seguir este patrón básico si decide implementar o debe implementar un try/finally
bloque, ya que el lenguaje de programación no admite una using
instrucción, pero permite llamadas directas al Dispose método .
Miembros de instancia de IDisposable
Si una clase posee un campo o propiedad de instancia y su tipo implementa IDisposable, la clase también debe implementar IDisposable. Para más información, consulte cómo implementar una eliminación en cascada.