Sdílet prostřednictvím


System.IDisposable – rozhraní

Tento článek obsahuje doplňující poznámky k referenční dokumentaci pro toto rozhraní API.

Primárním použitím IDisposable rozhraní je uvolnit nespravované prostředky. Uvolňování paměti automaticky uvolní paměť přidělenou spravovanému objektu, pokud se tento objekt už nepoužívá. Není však možné předpovědět, kdy dojde k uvolňování paměti. Uvolňování paměti navíc nemá žádné znalosti o nespravovaných prostředcích, jako jsou popisovače oken nebo otevírání souborů a datových proudů.

Dispose Pomocí metody tohoto rozhraní explicitně uvolnit nespravované prostředky ve spojení s uvolňováním paměti. Příjemce objektu může tuto metodu volat, pokud objekt již není potřeba.

Upozorňující

Jedná se o zásadní změnu přidání IDisposable rozhraní do existující třídy. Vzhledem k tomu, že již existující spotřebitelé typu nemohou volat Dispose, nemůžete být jisti, že nespravované prostředky uchovávané vaším typem budou uvolněny.

Vzhledem k tomu, že IDisposable.Dispose implementace je volána příjemcem typu, pokud prostředky vlastněné instancí již nejsou potřeba, měli byste buď zabalit spravovaný objekt do (doporučenou alternativu SafeHandle ), nebo byste měli přepsat Object.Finalize na volné nespravované prostředky v případě, že příjemce zapomene volat Dispose.

Důležité

V rozhraní .NET Framework kompilátor jazyka C++ podporuje deterministické odstranění prostředků a neumožňuje přímou Dispose implementaci metody.

Podrobné informace o tom, jak se toto rozhraní a Object.Finalize metoda používají, najdete v tématech o uvolňování paměti a implementaci metody Dispose.

Použití objektu, který implementuje IDisposable

Pokud vaše aplikace jednoduše používá objekt, který implementuje IDisposable rozhraní, měli byste po dokončení jeho použití volat implementaci objektu IDisposable.Dispose . V závislosti na programovacím jazyce to můžete udělat jedním ze dvou způsobů:

  • Pomocí konstruktoru jazyka, jako je příkaz using v jazyce C# a Visual Basic, a use příkazu nebo using funkce v jazyce F#.
  • Zabalením volání implementace IDisposable.Dispose do try/finally bloku.

Poznámka:

Dokumentace k typům, které implementují IDisposable tuto skutečnost, a obsahují připomenutí volání jeho Dispose implementace.

Příkaz C#, F# a Visual Basic Using

Pokud váš jazyk podporuje konstruktor, jako je příkaz using v jazyce C#, příkaz Using v jazyce Visual Basic nebo příkaz use v jazyce F#, můžete ho místo explicitního volání IDisposable.Dispose použít. Následující příklad používá tento přístup k definování WordCount třídy, která zachovává informace o souboru a počtu slov v něm.

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

Výraz using (use výraz v jazyce F#) je ve skutečnosti syntaktické pohodlí. Kompilátor jazyka v době kompilace implementuje zprostředkující try/finally jazyk (IL) pro blok.

Další informace o using příkazu naleznete v tématu Using Statement nebo using Statement témata.

Blok Try/Finally

Pokud programovací jazyk nepodporuje konstruktor, jako je příkaz using v jazyce C# nebo Visual Basic nebo use příkaz v jazyce F#, nebo pokud ho nechcete použít, můžete volat IDisposable.Dispose implementaci z finally bloku try/finally příkazu. Následující příklad nahrazuje using blok v předchozím příkladu blokem/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

Další informace o try/finally vzoru naleznete v tématu Vyzkoušet... Chytit... Příkaz Finally, try-finally, try... finally – výraz nebo try-finally – příkaz.

Implementace IDisposable

Pokud váš typ používá nespravované prostředky přímo nebo pokud chcete použít uvolnitelné prostředky sami, měli byste implementovat IDisposable . Příjemci vašeho typu můžou volat implementaci IDisposable.Dispose k bezplatným prostředkům, když už instanci nepotřebujete. Chcete-li zpracovat případy, ve kterých se jim nepodaří volat Dispose, měli byste buď použít třídu odvozenou z SafeHandle zabalení nespravovaných prostředků, nebo byste měli přepsat Object.Finalize metodu pro typ odkazu. V obou případech použijete metodu Dispose k provedení čištění, které je potřeba po použití nespravovaných prostředků, jako je uvolnění, uvolnění nebo resetování nespravovaných prostředků. Další informace o implementaci IDisposable.Dispose, viz Dispose(bool) přetížení metody.

Důležité

Pokud definujete základní třídu, která používá nespravované prostředky a které buď mají, nebo je pravděpodobné, že mají podtřídy, které by měly být uvolněny, měli byste implementovat metodu IDisposable.Dispose a poskytnout druhé přetížení Dispose, jak je popsáno v další části.

IDisposable a hierarchie dědičnosti

Základní třída s podtřídami, které by měly být uvolnitelné, musí implementovat IDisposable následujícím způsobem. Tento vzor byste měli použít vždy, když implementujete IDisposable jakýkoli typ, který není sealed (NotInheritable v jazyce Visual Basic).

  • Měla by poskytovat jednu veřejnou, ne virtuální Dispose() metodu a chráněnou virtuální Dispose(Boolean disposing) metodu.
  • Metoda Dispose() musí volat Dispose(true) a potlačit finalizaci pro výkon.
  • Základní typ by neměl obsahovat žádné finalizátory.

Následující fragment kódu odráží vzor Dispose pro základní třídy. Předpokládá se, že váš typ nepřepíše metodu Object.Finalize .

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

Pokud přepíšete metodu Object.Finalize , měla by vaše třída implementovat následující vzor.

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

Podtřídy by měly implementovat model na jedno použití následujícím způsobem:

  • Musí přepsat Dispose(Boolean) a volat implementaci základní třídy Dispose(Boolean) .
  • V případě potřeby mohou poskytnout finalizační metodu. Finalizátor musí zavolat Dispose(false).

Všimněte si, že odvozené třídy samy o sobě neimplementují IDisposable rozhraní a nezahrnují bezparametrovou Dispose metodu. Přepíší pouze metodu základní třídy Dispose(Boolean) .

Následující fragment kódu odráží vzor Dispose pro odvozené třídy. Předpokládá se, že váš typ nepřepíše metodu Object.Finalize .

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