Interfaz System.IDisposable
En este artículo se proporcionan comentarios adicionales a la documentación de referencia de esta API.
El uso principal de la IDisposable interfaz es liberar recursos no administrados. El recolector de elementos no utilizados libera automáticamente la memoria asignada a un objeto administrado cuando ese objeto ya no se usa. Sin embargo, no es posible predecir cuándo se producirá la recolección de elementos no utilizados. Además, el recolector de elementos no utilizados no tiene conocimiento de recursos no administrados, como identificadores de ventana, o archivos y secuencias abiertos.
Use el Dispose método de esta interfaz para liberar explícitamente recursos no administrados junto con el recolector de elementos no utilizados. El consumidor de un objeto puede llamar a este método cuando el objeto ya no es necesario.
Advertencia
Se trata de un cambio importante para agregar la IDisposable interfaz a una clase existente. Dado que los consumidores preexistentes del tipo no pueden llamar a Dispose, no puede estar seguro de que se liberarán los recursos no administrados mantenidos por el tipo.
Dado que el consumidor llama a la IDisposable.Dispose implementación de un tipo cuando los recursos que pertenecen a una instancia ya no son necesarios, debe encapsular el objeto administrado en ( SafeHandle la alternativa recomendada) o reemplazar para liberar Object.Finalize recursos no administrados en caso de que el consumidor olvide llamar Disposea .
Importante
En .NET Framework, el compilador de C++ admite la eliminación determinista de recursos y no permite la implementación directa del Dispose método.
Para obtener una explicación detallada sobre cómo se usa esta interfaz y el Object.Finalize método, consulte los temas Recolección de elementos no utilizados e Implementación de un método Dispose.
Uso de un objeto que implementa IDisposable
Si la aplicación simplemente usa un objeto que implementa la IDisposable interfaz, debe llamar a la implementación del IDisposable.Dispose objeto cuando haya terminado de usarlo. En función del lenguaje de programación, puede hacerlo de una de estas dos maneras:
- Mediante una construcción de lenguaje como la
using
instrucción en C# y Visual Basic, y lause
instrucción ousing
función en F#. - Al ajustar la llamada a la IDisposable.Dispose implementación en un
try
/finally
bloque.
Nota:
La documentación de los tipos que implementan IDisposable tenga en cuenta ese hecho e incluya un recordatorio para llamar a su Dispose implementación.
Instrucción Using de C#, F#y Visual Basic
Si el lenguaje admite una construcción como la instrucción using en C#, la instrucción Using en Visual Basic o la instrucción use en F#, puede usarla en lugar de llamarse IDisposable.Dispose explícitamente. En el ejemplo siguiente se usa este enfoque para definir una WordCount
clase que conserva información sobre un archivo y el número de palabras que hay en él.
using System;
using System.IO;
using System.Text.RegularExpressions;
public class WordCount
{
private String filename = String.Empty;
private int nWords = 0;
private String pattern = @"\b\w+\b";
public WordCount(string filename)
{
if (!File.Exists(filename))
throw new FileNotFoundException("The file does not exist.");
this.filename = filename;
string txt = String.Empty;
using (StreamReader sr = new StreamReader(filename))
{
txt = sr.ReadToEnd();
}
nWords = Regex.Matches(txt, pattern).Count;
}
public string FullName
{ get { return filename; } }
public string Name
{ get { return Path.GetFileName(filename); } }
public int Count
{ get { return nWords; } }
}
open System.IO
open System.Text.RegularExpressions
type WordCount(filename) =
let txt =
if File.Exists filename |> not then
raise (FileNotFoundException "The file does not exist.")
use sr = new StreamReader(filename)
sr.ReadToEnd()
let pattern = @"\b\w+\b"
let nWords = Regex.Matches(txt, pattern).Count
member _.FullName = filename
member _.Name = Path.GetFileName filename
member _.Count = nWords
Imports System.IO
Imports System.Text.RegularExpressions
Public Class WordCount
Private filename As String
Private nWords As Integer
Private pattern As String = "\b\w+\b"
Public Sub New(filename As String)
If Not File.Exists(filename) Then
Throw New FileNotFoundException("The file does not exist.")
End If
Me.filename = filename
Dim txt As String = String.Empty
Using sr As New StreamReader(filename)
txt = sr.ReadToEnd()
End Using
nWords = Regex.Matches(txt, pattern).Count
End Sub
Public ReadOnly Property FullName As String
Get
Return filename
End Get
End Property
Public ReadOnly Property Name As String
Get
Return Path.GetFileName(filename)
End Get
End Property
Public ReadOnly Property Count As Integer
Get
Return nWords
End Get
End Property
End Class
La using
instrucción (use
expresión en F#) es realmente una comodidad sintáctica. En tiempo de compilación, el compilador de lenguaje implementa el lenguaje intermedio (IL) para un try
/finally
bloque.
Para obtener más información sobre la using
instrucción , vea los temas Using Statement o using Statement .
El bloque Try/Finally
Si el lenguaje de programación no admite una construcción como la using
instrucción en C# o Visual Basic, o la use
instrucción en F#, o si prefiere no usarla, puede llamar a la IDisposable.Dispose implementación desde el finally
bloque de una try
/finally
instrucción. En el ejemplo siguiente se reemplaza el using
bloque del ejemplo anterior por un try
/finally
bloque .
using System;
using System.IO;
using System.Text.RegularExpressions;
public class WordCount2
{
private String filename = String.Empty;
private int nWords = 0;
private String pattern = @"\b\w+\b";
public WordCount2(string filename)
{
if (!File.Exists(filename))
throw new FileNotFoundException("The file does not exist.");
this.filename = filename;
string txt = String.Empty;
StreamReader? sr = null;
try
{
sr = new StreamReader(filename);
txt = sr.ReadToEnd();
}
finally
{
if (sr != null) sr.Dispose();
}
nWords = Regex.Matches(txt, pattern).Count;
}
public string FullName
{ get { return filename; } }
public string Name
{ get { return Path.GetFileName(filename); } }
public int Count
{ get { return nWords; } }
}
open System.IO
open System.Text.RegularExpressions
type WordCount2(filename) =
let txt =
if File.Exists filename |> not then
raise (FileNotFoundException "The file does not exist.")
let sr = new StreamReader(filename)
try
sr.ReadToEnd()
finally
sr.Dispose()
let pattern = @"\b\w+\b"
let nWords = Regex.Matches(txt, pattern).Count
member _.FullName = filename
member _.Name = Path.GetFileName filename
member _.Count = nWords
Imports System.IO
Imports System.Text.RegularExpressions
Public Class WordCount2
Private filename As String
Private nWords As Integer
Private pattern As String = "\b\w+\b"
Public Sub New(filename As String)
If Not File.Exists(filename) Then
Throw New FileNotFoundException("The file does not exist.")
End If
Me.filename = filename
Dim txt As String = String.Empty
Dim sr As StreamReader = Nothing
Try
sr = New StreamReader(filename)
txt = sr.ReadToEnd()
Finally
If sr IsNot Nothing Then sr.Dispose()
End Try
nWords = Regex.Matches(txt, pattern).Count
End Sub
Public ReadOnly Property FullName As String
Get
Return filename
End Get
End Property
Public ReadOnly Property Name As String
Get
Return Path.GetFileName(filename)
End Get
End Property
Public ReadOnly Property Count As Integer
Get
Return nWords
End Get
End Property
End Class
Para obtener más información sobre el try
/finally
patrón, vea Probar... Atrapar... Finally Statement, try-finally, try... finally Expression, o try-finally Statement.
Implementación de IDisposable
Debe implementar IDisposable si el tipo usa recursos no administrados directamente o si desea usar recursos descartables usted mismo. Los consumidores del tipo pueden llamar a la IDisposable.Dispose implementación para liberar recursos cuando la instancia ya no sea necesaria. Para controlar los casos en los que no pueden llamar Disposea , debe usar una clase derivada de SafeHandle para encapsular los recursos no administrados, o bien debe invalidar el Object.Finalize método para un tipo de referencia. En cualquier caso, se usa el Dispose método para realizar cualquier limpieza necesaria después de usar los recursos no administrados, como liberar, liberar o restablecer los recursos no administrados. Para obtener más información sobre la implementación IDisposable.Disposede , consulte la sobrecarga del método Dispose(bool).
Importante
Si va a definir una clase base que usa recursos no administrados y que tiene, o es probable que tenga, subclases que se deben eliminar, debe implementar el IDisposable.Dispose método y proporcionar una segunda sobrecarga de Dispose
, como se describe en la sección siguiente.
IDisposable y la jerarquía de herencia
Una clase base con subclases que debe ser descartable debe implementar IDisposable de la siguiente manera. Debe usar este patrón siempre que implemente IDisposable en cualquier tipo que no sealed
sea (NotInheritable
en Visual Basic).
- Debe proporcionar un método público, no virtual Dispose() y un método virtual
Dispose(Boolean disposing)
protegido. - El Dispose() método debe llamar a
Dispose(true)
y debe suprimir la finalización del rendimiento. - El tipo base no debe incluir ningún finalizador.
El siguiente fragmento de código refleja el patrón dispose para las clases base. Se supone que el tipo no invalida el Object.Finalize método .
using System;
using System.IO;
using System.Runtime.InteropServices;
class BaseClass1 : IDisposable
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Instantiate a FileStream instance.
FileStream fs = new FileStream("test.txt", FileMode.OpenOrCreate);
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
fs.Dispose();
// Free any other managed objects here.
//
}
disposed = true;
}
}
open System
open System.IO
type BaseClass1() =
// Flag: Has Dispose already been called?
let mutable disposed = false
// Instantiate a FileStream instance.
let fs = new FileStream("test.txt", FileMode.OpenOrCreate)
interface IDisposable with
// Public implementation of Dispose pattern callable by consumers.
member this.Dispose() =
this.Dispose true
GC.SuppressFinalize this
// Implementation of Dispose pattern.
abstract Dispose: bool -> unit
override _.Dispose(disposing) =
if not disposed then
if disposing then
fs.Dispose()
// Free any other managed objects here.
disposed <- true
Imports System.IO
Imports System.Runtime.InteropServices
Class BaseClass1 : Implements IDisposable
' Flag: Has Dispose already been called?
Dim disposed As Boolean = False
' Instantiate a FileStream instance.
Dim fs As FileStream = New FileStream("test.txt", FileMode.OpenOrCreate)
' Public implementation of Dispose pattern callable by consumers.
Public Sub Dispose() _
Implements IDisposable.Dispose
Dispose(disposing:=True)
GC.SuppressFinalize(Me)
End Sub
' Protected implementation of Dispose pattern.
Protected Overridable Sub Dispose(disposing As Boolean)
If disposed Then Return
If disposing Then
fs.Dispose()
' Free any other managed objects here.
'
End If
disposed = True
End Sub
End Class
Si reemplaza el Object.Finalize método , la clase debe implementar el siguiente patrón.
using System;
class BaseClass2 : IDisposable
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
// Free any other managed objects here.
//
}
// Free any unmanaged objects here.
//
disposed = true;
}
~BaseClass2()
{
Dispose(disposing: false);
}
}
open System
type BaseClass2() =
// Flag: Has Dispose already been called?
let mutable disposed = false
interface IDisposable with
// Public implementation of Dispose pattern callable by consumers.
member this.Dispose() =
this.Dispose true
GC.SuppressFinalize this
// Implementation of Dispose pattern.
abstract Dispose: bool -> unit
override _.Dispose(disposing) =
if not disposed then
if disposing then
// Free any other managed objects here.
()
// Free any unmanaged objects here.
disposed <- true
override this.Finalize() =
this.Dispose false
Class BaseClass : Implements IDisposable
' Flag: Has Dispose already been called?
Dim disposed As Boolean = False
' Public implementation of Dispose pattern callable by consumers.
Public Sub Dispose() _
Implements IDisposable.Dispose
Dispose(disposing:=True)
GC.SuppressFinalize(Me)
End Sub
' Protected implementation of Dispose pattern.
Protected Overridable Sub Dispose(disposing As Boolean)
If disposed Then Return
If disposing Then
' Free any other managed objects here.
'
End If
' Free any unmanaged objects here.
'
disposed = True
End Sub
Protected Overrides Sub Finalize()
Dispose(disposing:=False)
End Sub
End Class
Las subclases deben implementar el patrón desechable de la siguiente forma:
- Deben invalidar
Dispose(Boolean)
y llamar a la implementaciónDispose(Boolean)
de la clase base. - Pueden proporcionar un finalizador si es necesario. El finalizador debe llamar a
Dispose(false)
.
Tenga en cuenta que las clases derivadas no implementan la IDisposable interfaz y no incluyen un método sin Dispose parámetros. Solo invalidan el método de clase Dispose(Boolean)
base.
El fragmento de código siguiente refleja el patrón dispose para las clases derivadas. Se supone que el tipo no invalida el Object.Finalize método .
using System;
using System.IO;
using System.Runtime.InteropServices;
class MyDerivedClass : MyBaseClass
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Instantiate a FileStream instance.
FileStream fs = new FileStream("test.txt", FileMode.OpenOrCreate);
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
fs.Dispose();
// Free any other managed objects here.
//
}
// Free any unmanaged objects here.
//
disposed = true;
// Call base class implementation.
base.Dispose(disposing);
}
}
open Microsoft.Win32.SafeHandles
open System
type MyDerivedClass() =
inherit MyBaseClass()
// Flag: Has Dispose already been called?
let mutable disposed = false
// Instantiate a FileStream instance.
let fs = new FileStream("test.txt", FileMode.OpenOrCreate)
// Implementation of Dispose pattern.
override _.Dispose(disposing) =
if not disposed then
if disposing then
fs.Dispose()
// Free any other managed objects here.
// Free any unmanaged objects here.
disposed <- true
// Call base class implementation.
base.Dispose disposing
Imports System.IO
Imports System.Runtime.InteropServices
Class DerivedClass2 : Inherits BaseClass2
' Flag: Has Dispose already been called?
Dim disposed As Boolean = False
' Instantiate a FileStream instance.
Dim fs As FileStream = New FileStream("test.txt", FileMode.OpenOrCreate)
' Protected implementation of Dispose pattern.
Protected Overrides Sub Dispose(disposing As Boolean)
If disposed Then Return
If disposing Then
fs.Dispose()
' Free any other managed objects here.
'
End If
' Free any unmanaged objects here.
'
disposed = True
' Call base class implementation.
MyBase.Dispose(disposing)
End Sub
End Class