Freigeben über


System.IDisposable-Schnittstelle

Dieser Artikel enthält ergänzende Hinweise zur Referenzdokumentation für diese API.

Die primäre Verwendung der IDisposable Schnittstelle besteht darin, nicht verwaltete Ressourcen freizugeben. Der Garbage Collector gibt automatisch den Speicher frei, der einem verwalteten Objekt zugeordnet ist, wenn dieses Objekt nicht mehr verwendet wird. Es ist jedoch nicht möglich, vorherzusagen, wann die Garbage Collection stattfindet. Darüber hinaus hat der Garbage Collector keine Kenntnis von nicht verwalteten Ressourcen wie Fenster-Handles oder offenen Dateien und Datenströmen.

Verwenden Sie die Dispose Methode dieser Schnittstelle, um nicht verwaltete Ressourcen in Verbindung mit dem Garbage Collector explizit freizugeben. Der Verbraucher eines Objekts kann diese Methode aufrufen, wenn das Objekt nicht mehr benötigt wird.

Warnung

Es handelt sich um eine bahnbrechende Änderung, um die IDisposable Schnittstelle zu einer vorhandenen Klasse hinzuzufügen. Da bereits existierende Consumer Ihres Typs nicht Dispose aufrufen können, können Sie nicht sicher sein, dass nicht verwaltete Ressourcen, die Ihr Typ besitzt, freigegeben werden.

Da die IDisposable.Dispose Implementierung vom Verbraucher eines Typs aufgerufen wird, wenn die Ressourcen, die einer Instanz gehören, nicht mehr benötigt werden, sollten Sie entweder das verwaltete Objekt in eine SafeHandle (die empfohlene Alternative) einschließen, oder Sie sollten Object.Finalize überschreiben, damit im Fall, dass der Verbraucher den Aufruf von Dispose vergisst, nicht verwaltete Ressourcen freigegeben werden.

Von Bedeutung

In .NET Framework unterstützt der C++-Compiler die deterministische Entsorgung von Ressourcen und lässt keine direkte Implementierung der Dispose Methode zu.

Eine ausführliche Erläuterung dazu, wie diese Schnittstelle und die Object.Finalize Methode verwendet werden, finden Sie in den Themen " Garbage Collection " und "Implementieren einer Dispose-Methode" .

Verwenden eines Objekts, das IDisposable implementiert

Wenn Ihre App einfach ein Objekt verwendet, das die IDisposable Schnittstelle implementiert, sollten Sie die Implementierung des IDisposable.Dispose Objekts aufrufen, wenn Sie damit fertig sind. Je nach Programmiersprache können Sie dies auf eine von zwei Arten tun:

  • Mithilfe eines Sprachkonstrukts, z. B. der using Anweisung in C# und Visual Basic, und der use Anweisung oder using Funktion in F#
  • Indem Sie den Aufruf der IDisposable.Dispose-Implementierung in einen try/finally-Block verpacken.

Hinweis

Die Dokumentation für Typen, die IDisposable implementieren, weist auf diese Tatsache hin und enthält einen Hinweis auf den Aufruf der Dispose-Implementierung.

Die C#, F# und Visual Basic Anweisung verwenden

Wenn Ihre Sprache ein Konstrukt wie die using-Anweisung in C#, die Using-Anweisung in Visual Basic oder die Use-Anweisung in F# unterstützt, können Sie es verwenden, anstatt sie explizit selbst aufzurufen IDisposable.Dispose . Im folgenden Beispiel wird dieser Ansatz verwendet, um eine WordCount Klasse zu definieren, die Informationen zu einer Datei und die Anzahl der darin enthaltenen Wörter bewahrt.

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

Die using-Anweisung (use-Ausdruck in F#) ist eigentlich eine syntaktische Bequemlichkeit. Während der Kompilierung implementiert der Sprach-Compiler die Zwischen-Sprache (IL) für einen try/finally Block.

Weitere Informationen über die usingAnweisung finden Sie unter den Themen Anweisung verwenden oder Anweisung verwenden.

Der Try/Finally-Block

Wenn Ihre Programmiersprache kein Konstrukt wie die using Anweisung in C# oder Visual Basic oder die use Anweisung in F# unterstützt oder sie nicht verwenden möchte, können Sie die IDisposable.Dispose Implementierung aus dem finally Block einer try/finally Anweisung aufrufen. Im folgenden Beispiel wird der using Block im vorherigen Beispiel durch einen try/finally Block ersetzt.

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

Weitere Informationen zum try/finally Pattern finden Sie unter Try...Catch...Finally-Anweisung, try-finally, try...finally-Ausdruck oder try-finally-Anweisung.

Implementieren von IDisposable

Sie sollten IDisposable implementieren, wenn Ihr Typ nicht verwaltete Ressourcen direkt verwendet oder wenn Sie wegwerfbare Ressourcen selbst verwenden möchten. Die Verbraucher Ihres Typs können Ihre IDisposable.Dispose Implementierung aufrufen, um Ressourcen freizugeben, wenn die Instanz nicht mehr benötigt wird. Um Fälle zu behandeln, in denen sie Dispose nicht aufrufen, sollten Sie entweder eine von der Klasse SafeHandle abgeleitete Klasse verwenden, um die nicht verwalteten Ressourcen zu kapseln, oder Sie sollten die Object.Finalize Methode für einen Verweistyp überschreiben. In beiden Fällen verwenden Sie die Dispose Methode, um die erforderlichen Bereinigungen auszuführen, nachdem Sie die nicht verwalteten Ressourcen verwendet haben, wie z. B. das Freigeben, Beenden oder Zurücksetzen der nicht verwalteten Ressourcen. Weitere Informationen zur Implementierung von IDisposable.Dispose finden Sie unter Überladung der Methode Dispose(bool).

Von Bedeutung

Wenn Sie eine Basisklasse definieren, die nicht verwaltete Ressourcen verwendet und entweder über Unterklassen verfügt, die verworfen werden müssen, oder voraussichtlich über solche Unterklassen verfügen wird, sollten Sie die IDisposable.Dispose Methode implementieren und, wie im nächsten Abschnitt beschrieben, eine zweite Überladung von Dispose bereitstellen.

IDisposable und die Vererbungshierarchie

Eine Basisklasse mit Unterklassen, die verfügbar sein sollten, muss wie folgt implementiert IDisposable werden. Sie sollten dieses Muster immer dann verwenden, wenn Sie IDisposable bei einem beliebigen Typ implementieren, der nicht sealed (NotInheritable in Visual Basic) ist.

  • Sie sollte eine öffentliche, nicht virtuelle Dispose() Methode und eine geschützte virtuelle Dispose(Boolean disposing) Methode bereitstellen.
  • Die Methode Dispose() muss Dispose(true) aufrufen und sollte die Finalisierung aus Leistungsgründen unterdrücken.
  • Der Basistyp sollte keine Finalizer enthalten.

Das folgende Codefragment spiegelt das Dispose-Muster für Basisklassen wider. Es wird davon ausgegangen, dass Ihr Typ die Object.Finalize Methode nicht überschreibt.

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

Wenn Sie die Object.Finalize Methode außer Kraft setzen, sollte Ihre Klasse das folgende Muster implementieren.

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

Unterklassen sollten das verwerfbare Muster wie folgt implementieren:

  • Sie müssen Dispose(Boolean) überschreiben und die Basisklassenimplementierung Dispose(Boolean) aufrufen.
  • Sie können bei Bedarf einen Finalizer bereitstellen. Der Finalizer muss Dispose(false) aufrufen.

Beachten Sie, dass abgeleitete Klassen die Schnittstelle nicht selbst implementieren IDisposable und keine parameterlose Dispose Methode enthalten. Sie überschreiben nur die Basisklassenmethode Dispose(Boolean) .

Das folgende Codefragment gibt das Dispose-Muster für abgeleitete Klassen wieder. Es wird davon ausgegangen, dass Ihr Typ die Object.Finalize Methode nicht überschreibt.

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