Condividi tramite


Interfaccia System.IDisposable

Questo articolo fornisce osservazioni supplementari alla documentazione di riferimento per questa API.

L'uso principale dell'interfaccia consiste nel IDisposable rilasciare risorse non gestite. Il Garbage Collector rilascia automaticamente la memoria allocata a un oggetto gestito quando tale oggetto non viene più usato. Tuttavia, non è possibile prevedere quando si verificherà l'operazione di Garbage Collection. Inoltre, Il Garbage Collector non conosce risorse non gestite, ad esempio handle di finestra o file e flussi aperti.

Usare il Dispose metodo di questa interfaccia per rilasciare in modo esplicito le risorse non gestite insieme al Garbage Collector. Il consumer di un oggetto può chiamare questo metodo quando l'oggetto non è più necessario.

Avviso

Si tratta di una modifica che causa un'interruzione per aggiungere l'interfaccia IDisposable a una classe esistente. Poiché i consumer preesistenti del tipo non possono chiamare Dispose, non è possibile essere certi che le risorse non gestite mantenute dal tipo verranno rilasciate.

Poiché l'implementazione IDisposable.Dispose viene chiamata dal consumer di un tipo quando le risorse di proprietà di un'istanza non sono più necessarie, è necessario eseguire il wrapping dell'oggetto gestito in un SafeHandle oggetto (l'alternativa consigliata) oppure eseguire l'override per liberare Object.Finalize risorse non gestite nel caso in cui il consumer dimentica di chiamare Dispose.

Importante

In .NET Framework il compilatore C++ supporta l'eliminazione deterministica delle risorse e non consente l'implementazione diretta del Dispose metodo.

Per informazioni dettagliate sull'uso di questa interfaccia e del Object.Finalize metodo, vedere gli argomenti Garbage Collection e Implementazione di un metodo Dispose.

Usare un oggetto che implementa IDisposable

Se l'app usa semplicemente un oggetto che implementa l'interfaccia IDisposable , devi chiamare l'implementazione dell'oggetto al termine dell'uso IDisposable.Dispose . A seconda del linguaggio di programmazione, è possibile eseguire questa operazione in uno dei due modi seguenti:

  • Usando un costrutto di linguaggio, ad esempio l'istruzione using in C# e Visual Basic, e l'istruzione o using la use funzione in F#.
  • Eseguendo il wrapping della chiamata all'implementazione IDisposable.Dispose in un try/finally blocco.

Nota

Documentazione per i tipi che implementano IDisposable si noti che il fatto e includere un promemoria per chiamare la relativa Dispose implementazione.

Istruzione Using C#, F#e Visual Basic

Se il linguaggio supporta un costrutto come l'istruzione using in C#, l'istruzione Using in Visual Basic o l'istruzione use in F#, è possibile usarla invece di chiamare IDisposable.Dispose in modo esplicito se stessi. Nell'esempio seguente viene usato questo approccio per definire una WordCount classe che mantiene le informazioni su un file e il numero di parole in esso contenute.

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

L'istruzione using (use espressione in F#) è in realtà una praticità sintattica. In fase di compilazione, il compilatore del linguaggio implementa il linguaggio intermedio (IL) per un try/finally blocco.

Per altre informazioni sull'istruzione using , vedere gli argomenti Using Statement o using Statement .

Blocco Try/Finally

Se il linguaggio di programmazione non supporta un costrutto come l'istruzione using in C# o Visual Basic o l'istruzione use in F#, oppure se si preferisce non usarlo, è possibile chiamare l'implementazione IDisposable.Dispose dal finally blocco di un'istruzione/tryfinally. Nell'esempio seguente il using blocco dell'esempio precedente viene sostituito con un try/finally blocco .

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

Per altre informazioni sul try/finally modello, vedere Prova... Prendere... Infine Istruzione, try-finally, try... finally Expression o try-finally Statement.

Implementare IDisposable

È consigliabile implementare IDisposable se il tipo usa direttamente le risorse non gestite o se si desidera usare manualmente le risorse eliminabili. I consumer del tipo possono chiamare l'implementazione IDisposable.Dispose per liberare risorse quando l'istanza non è più necessaria. Per gestire i casi in cui non riescono a chiamare Dispose, è necessario usare una classe derivata da SafeHandle per eseguire il wrapping delle risorse non gestite oppure eseguire l'override del Object.Finalize metodo per un tipo di riferimento. In entrambi i casi, si usa il Dispose metodo per eseguire qualsiasi pulizia necessaria dopo aver usato le risorse non gestite, ad esempio liberando, rilasciando o reimpostando le risorse non gestite. Per altre informazioni sull'implementazione IDisposable.Disposedi , vedere l'overload del metodo Dispose(bool).

Importante

Se si definisce una classe di base che usa risorse non gestite e che ha, o probabilmente, sottoclassi che devono essere eliminate, è necessario implementare il IDisposable.Dispose metodo e fornire un secondo overload di Dispose, come illustrato nella sezione successiva.

IDisposable e la gerarchia di ereditarietà

Una classe base con sottoclassi che devono essere eliminabili deve essere implementata IDisposable come indicato di seguito. È consigliabile usare questo modello ogni volta che si implementa IDisposable su qualsiasi tipo che non sealed sia (NotInheritable in Visual Basic).

  • Deve fornire un metodo pubblico, non virtuale Dispose() e un metodo virtuale Dispose(Boolean disposing) protetto.
  • Il Dispose() metodo deve chiamare Dispose(true) e eliminare la finalizzazione per le prestazioni.
  • Il tipo di base non deve includere alcun finalizzatori.

Il frammento di codice seguente riflette il modello dispose per le classi di base. Si presuppone che il tipo non esegue l'override del Object.Finalize metodo .

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

Se si esegue l'override del Object.Finalize metodo , la classe deve implementare il modello seguente.

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

Le sottoclassi devono implementare il modello Disposable nel modo seguente:

  • Devono eseguire l'override di Dispose(Boolean) e chiamare l'implementazione Dispose(Boolean) della classe di base.
  • Se necessario, possono fornire un finalizzatore. Il finalizzatore deve chiamare Dispose(false).

Si noti che le classi derivate non implementano l'interfaccia IDisposable e non includono un metodo senza Dispose parametri. Eseguono solo l'override del metodo della classe Dispose(Boolean) base.

Il frammento di codice seguente riflette il modello dispose per le classi derivate. Si presuppone che il tipo non esegue l'override del Object.Finalize metodo .

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