System.IDisposable, interfejs

Ten artykuł zawiera dodatkowe uwagi dotyczące dokumentacji referencyjnej dla tego interfejsu API.

Podstawowym zastosowaniem interfejsu IDisposable jest zwolnienie niezarządzanych zasobów. Moduł odśmiecający pamięć automatycznie zwalnia pamięć przydzieloną do obiektu zarządzanego, gdy ten obiekt nie jest już używany. Nie można jednak przewidzieć, kiedy nastąpi odzyskiwanie pamięci. Ponadto moduł odśmiecanie pamięci nie ma wiedzy na temat niezarządzanych zasobów, takich jak uchwyty okien, ani otwieranie plików i strumieni.

Dispose Użyj metody tego interfejsu, aby jawnie zwolnić niezarządzane zasoby w połączeniu z modułem odśmiecanie pamięci. Użytkownik obiektu może wywołać tę metodę, gdy obiekt nie jest już potrzebny.

Ostrzeżenie

Jest to zmiana powodująca niezgodność, aby dodać IDisposable interfejs do istniejącej klasy. Ponieważ wstępnie istniejący użytkownicy typu nie mogą wywołać Disposemetody , nie można mieć pewności, że niezarządzane zasoby przechowywane przez typ zostaną zwolnione.

Ponieważ implementacja IDisposable.Dispose jest wywoływana przez użytkownika typu, gdy zasoby należące do wystąpienia nie są już potrzebne, należy opakować obiekt zarządzany w (zalecaną SafeHandle alternatywę) lub przesłonić Object.Finalize bezpłatne niezarządzane zasoby w przypadku, gdy użytkownik zapomni wywołać metodę Dispose.

Ważne

W programie .NET Framework kompilator języka C++ obsługuje deterministyczną usuwanie zasobów i nie zezwala na bezpośrednią implementację Dispose metody.

Aby zapoznać się ze szczegółowym omówieniem sposobu użycia tego interfejsu Object.Finalize i metody, zobacz temat Odzyskiwanie pamięci i Implementowanie metody usuwania.

Używanie obiektu implementujące interfejs IDisposable

Jeśli aplikacja używa po prostu obiektu, który implementuje IDisposable interfejs, należy wywołać implementację obiektu IDisposable.Dispose po zakończeniu korzystania z niego. W zależności od języka programowania można to zrobić na jeden z dwóch sposobów:

  • Korzystając z konstrukcji językowej, takiej jak using instrukcja w języku C# i Visual Basic, oraz use instrukcja lub using funkcja w języku F#.
  • Zawijając wywołanie do implementacji IDisposable.Dispose w try/finally bloku.

Uwaga

Dokumentacja typów, które implementują IDisposable fakt i zawierają przypomnienie o wywołaniu jego Dispose implementacji.

Instrukcja Using języka C#, F# i Visual Basic

Jeśli język obsługuje konstrukcję, taką jak instrukcja using w języku C#, instrukcja Using w Visual Basic lub instrukcja use w języku F#, możesz użyć jej zamiast jawnie wywoływać IDisposable.Dispose siebie. W poniższym przykładzie użyto tego podejścia w zdefiniowaniu WordCount klasy, która zachowuje informacje o pliku i liczbie wyrazów w nim.

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

Instrukcja using (use wyrażenie w języku F#) jest w rzeczywistości wygodą składniową. W czasie kompilacji kompilator języka implementuje język pośredni (IL) dla try/finally bloku.

Aby uzyskać więcej informacji na temat instrukcji using , zobacz tematy Using Statement lub using Statement .

Blok Try/Finally

Jeśli język programowania nie obsługuje konstrukcji takiej jak using instrukcja w języku C# lub Visual Basic albo use instrukcja w języku F#, lub jeśli nie chcesz jej używać, możesz wywołać IDisposable.Dispose implementację z finally bloku try/finally instrukcji. Poniższy przykład zastępuje using blok w poprzednim przykładzie blok blokiem/tryfinally.

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

Aby uzyskać więcej informacji na temat try/finally wzorca, zobacz Try... Złapać... Na koniec instrukcja try-finally, spróbuj... wreszcie wyrażenie lub try-finally, instrukcja.

Implementowanie funkcji IDisposable

Należy zaimplementować IDisposable , jeśli typ używa zasobów niezarządzanych bezpośrednio lub jeśli chcesz samodzielnie korzystać z zasobów jednorazowych. Użytkownicy typu mogą wywołać implementację IDisposable.Dispose , aby zwolnić zasoby, gdy wystąpienie nie jest już potrzebne. Aby obsłużyć przypadki, w których nie można wywołać Disposemetody , należy użyć klasy pochodzącej z SafeHandle klasy w celu opakowania niezarządzanych zasobów lub należy zastąpić Object.Finalize metodę dla typu odwołania. W obu przypadkach użyjesz Dispose metody , aby wykonać niezależnie od tego, co jest konieczne po użyciu niezarządzanych zasobów, takich jak zwalnianie, zwalnianie lub resetowanie niezarządzanych zasobów. Aby uzyskać więcej informacji na temat implementowania IDisposable.Disposemetody , zobacz przeciążenie metody Dispose(bool).

Ważne

Jeśli definiujesz klasę bazową, która używa niezarządzanych zasobów i która ma lub prawdopodobnie ma, podklasy, które powinny zostać usunięte, należy zaimplementować IDisposable.Dispose metodę i podać drugie przeciążenie Disposeklasy , jak opisano w następnej sekcji.

IDisposable i hierarchia dziedziczenia

Klasa bazowa z podklasami, które powinny być jednorazowe, musi zostać zaimplementowana IDisposable w następujący sposób. Ten wzorzec należy używać zawsze, gdy implementujesz IDisposable dowolny typ, który nie sealed jest (NotInheritable w Visual Basic).

  • Powinna ona udostępnić jedną publiczną, niewirtuacyjną Dispose() metodę i chronioną metodę wirtualną Dispose(Boolean disposing) .
  • Metoda Dispose() musi wywołać Dispose(true) metodę i powinna pominąć finalizację wydajności.
  • Typ podstawowy nie powinien zawierać żadnych finalizatorów.

Poniższy fragment kodu odzwierciedla wzorzec usuwania dla klas bazowych. Przyjęto założenie, że typ nie zastępuje Object.Finalize metody .

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

Jeśli zastąpisz metodę Object.Finalize , klasa powinna zaimplementować następujący wzorzec.

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

Podklasy powinny implementować wzorzec jednorazowego w następujący sposób:

  • Muszą one zastąpić Dispose(Boolean) i wywołać implementację klasy Dispose(Boolean) bazowej.
  • W razie potrzeby mogą dostarczyć finalizator. Finalizator musi wywołać metodę Dispose(false).

Należy pamiętać, że klasy pochodne nie implementują interfejsu IDisposable i nie zawierają metody bez Dispose parametrów. Zastępują tylko metodę klasy Dispose(Boolean) bazowej.

Poniższy fragment kodu odzwierciedla wzorzec usuwania dla klas pochodnych. Przyjęto założenie, że typ nie zastępuje Object.Finalize metody .

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