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 la use instrucción o using 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ón Dispose(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