Интерфейс System.IDisposable

В этой статье приводятся дополнительные замечания к справочной документации по этому API.

Основное использование IDisposable интерфейса заключается в выпуске неуправляемых ресурсов. Сборщик мусора автоматически освобождает память, выделенную управляемому объекту, когда этот объект больше не используется. Однако невозможно предсказать, когда будет происходить сборка мусора. Кроме того, сборщик мусора не знает о неуправляемых ресурсах, таких как дескрипторы окон, или открывать файлы и потоки.

Dispose Используйте этот интерфейс для явного освобождения неуправляемых ресурсов вместе с сборщиком мусора. Потребитель объекта может вызывать этот метод, если объект больше не нужен.

Предупреждение

Это критическое изменение для добавления IDisposable интерфейса в существующий класс. Так как уже существующие потребители вашего типа не могут вызываться Dispose, вы не можете быть уверены, что неуправляемые ресурсы, удерживаемые вашим типом, будут выпущены.

IDisposable.Dispose Так как реализация вызывается потребителем типа, когда ресурсы, принадлежащие экземпляру, больше не нужны, следует либо упаковать управляемый объект в SafeHandle (рекомендуемую альтернативу), либо переопределить Object.Finalize на освобождение неуправляемых ресурсов в случае, если потребитель забывает вызыватьDispose.

Важно!

В платформа .NET Framework компилятор C++ поддерживает детерминированное удаление ресурсов и не разрешает прямую реализацию Dispose метода.

Подробные сведения об использовании этого интерфейса и метода см. в разделе "Сборка мусора" Object.Finalize и "Реализация метода удаления".

Использование объекта, реализующего IDisposable

Если приложение просто использует объект, реализующий IDisposable интерфейс, необходимо вызвать реализацию объекта IDisposable.Dispose после завершения его использования. В зависимости от языка программирования это можно сделать одним из двух способов:

  • Используя языковую конструкцию, using например инструкцию в C# и Visual Basic, а use также инструкцию или using функцию в F#.
  • Завернув вызов IDisposable.Dispose реализации в try/finally блок.

Примечание.

Документация по типам, реализующим IDisposable этот факт, и включите напоминание о его Dispose реализации.

Инструкция using C#, F# и Visual Basic

Если язык поддерживает конструкцию, например инструкцию using в C#, инструкцию Using в Visual Basic или инструкцию use в F#, ее можно использовать вместо явного вызова IDisposable.Dispose самостоятельно. В следующем примере этот подход используется при определении WordCount класса, сохраняющего сведения о файле и количестве слов в нем.

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

Оператор using (use выражение в F#) на самом деле является синтаксическим удобством. Во время компиляции компилятор языка реализует промежуточный try/finally язык (IL) для блока.

Дополнительные сведения об инструкции using см. в разделах "Использование инструкции " или "Использование инструкций ".

Блок Try/Finally

Если язык программирования не поддерживает конструкцию, например using инструкцию в C# или Visual Basic, или use инструкцию в F#, или если вы предпочитаете не использовать ее, можно вызвать IDisposable.Dispose реализацию из finally блока инструкции try/finally . Следующий пример заменяет using блок в предыдущем примере блоком/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

Дополнительные сведения о шаблоне см. try/finally в статье "Попробуйте... Поймать... Наконец, оператор, try-finally, попробуйте... Наконец, выражение или инструкция try-finally.

Реализация IDisposable

Следует реализовать, IDisposable если тип использует неуправляемые ресурсы напрямую или если вы хотите самостоятельно использовать удаленные ресурсы. Потребители вашего типа могут вызывать реализацию IDisposable.Dispose для освобождения ресурсов, когда экземпляр больше не нужен. Для обработки случаев, в которых они не вызываются Dispose, следует либо использовать класс, производный от SafeHandle оболочки неуправляемых ресурсов, либо переопределить Object.Finalize метод для ссылочного типа. В любом случае метод используется Dispose для выполнения любой очистки после использования неуправляемых ресурсов, таких как освобождение, освобождение или сброс неуправляемых ресурсов. Дополнительные сведения о реализации IDisposable.Disposeсм. в перегрузке метода Dispose(bool).

Важно!

Если вы определяете базовый класс, использующий неуправляемые ресурсы и имеющий или имеющий, или, скорее всего, подклассы, которые должны быть удалены, следует реализовать IDisposable.Dispose метод и предоставить вторую перегрузку Dispose, как описано в следующем разделе.

IDisposable и иерархия наследования

Базовый класс с подклассами, которые должны быть удалены, должны реализовываться IDisposable следующим образом. Этот шаблон следует использовать всякий раз, когда вы реализуете IDisposable любой тип, который не sealed является (NotInheritable в Visual Basic).

  • Он должен предоставлять один общедоступный, не виртуальный Dispose() метод и защищенный виртуальный Dispose(Boolean disposing) метод.
  • Метод Dispose() должен вызываться Dispose(true) и должен подавлять завершение для производительности.
  • базовый тип не должен включать никакие методы завершения.

Следующий фрагмент кода отражает шаблон удаления базовых классов. Предполагается, что тип не переопределяет 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

При переопределении Object.Finalize метода класс должен реализовать следующий шаблон.

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

Подклассы должны реализовывать удаляемость следующим образом:

  • они должны переопределять Dispose(Boolean) и вызвать реализацию Dispose(Boolean) базового класса;
  • при необходимости они могут предоставлять метод завершения. Метод завершения должен вызвать Dispose(false).

Обратите внимание, что производные классы не реализуют IDisposable интерфейс и не включают метод без Dispose параметров. Они переопределяют только метод базового класса Dispose(Boolean) .

Следующий фрагмент кода отражает шаблон удаления производных классов. Предполагается, что тип не переопределяет 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