System.IDisposable 接口

本文提供了此 API 参考文档的补充说明。

接口IDisposable的主要用途是释放非托管资源。 垃圾回收器会在不再使用该对象时自动释放分配给托管对象的内存。 但是,无法预测何时发生垃圾回收。 此外,垃圾回收器对窗口句柄、打开的文件和数据流等非托管资源一无所知。

使用此接口的 Dispose 方法与垃圾收集器一起显式释放非托管资源。 当不再需要对象时,对象的使用者可以调用此方法。

警告

向现有类添加 IDisposable 接口是一项重大变更。 由于您类型的现有使用者无法调用 Dispose,因此无法确定类型持有的未管理资源是否会被释放。

由于 IDisposable.Dispose 实现会在不再需要某个实例所拥有的资源时被该类型的使用者调用,因此应该用 SafeHandle 封装托管对象(推荐的替代方法),或者覆盖 Object.Finalize 以在使用者忘记调用 Dispose 时释放未托管的资源。

重要

在 .NET Framework 中,C++编译器支持确定性资源处置,不允许直接实现 Dispose 该方法。

有关如何使用此接口和 Object.Finalize 方法的详细讨论,请参阅 垃圾回收实现 Dispose 方法 主题。

使用实现 IDisposable 的对象

如果应用只是使用实现 IDisposable 接口的对象,则应在使用完该对象后调用该对象的 IDisposable.Dispose 实现。 根据编程语言,可以通过以下两种方式之一执行此作:

  • 通过使用语言构造,例如在 C# 和 Visual Basic 中使用 using 语句,以及在 F# 中使用 use 语句或 using 函数。
  • 将对 IDisposable.Dispose 实现的调用封装在 try/finally 块中。

注释

实现 IDisposable 的类型的文档会指出这一事实,并提醒用户调用其 Dispose 实现。

C#、F# 和 Visual Basic Using 语句

如果你的语言支持 C# 中的 using 语句、Visual Basic 中的 Using 语句或 F# 中的 use 语句等构造,则可以使用它,而不是显式调用 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语句(useF# 中的表达式)实际上是语法便利。 在编译时,语言编译器为 try/finally 块实现中间语言(IL)。

有关 using 语句的更多信息,请参阅 Using 语句使用 Statement 主题。

Try/Finally 块

如果您的编程语言不支持像 C# 或 Visual Basic 中的using语句,或 F# 中的use语句这样的构造,或者您不想使用它,则可以从IDisposable.Disposefinallytry语句块中调用/实现。 以下示例将上一示例中的using块替换为块try/finally

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...Catch...Finally 语句try-finallytry...finally 表达式,或 try-finally 语句

实现 IDisposable

如果您的类型直接使用非托管资源,或者如果您希望自己使用可释放资源,则应实现 IDisposable 。 不再需要该实例时,类型使用者可调用 IDisposable.Dispose 实现来释放资源。 要处理调用 Dispose 失败的情况,应使用从 SafeHandle 派生的类来封装非托管资源,或者替代引用类型的 Object.Finalize 方法。 在任何一种情况下,你都可以使用 Dispose 方法在使用非托管资源后执行任何必要的清理,例如释放、松开或重置这些资源。 有关实现 IDisposable.Dispose的详细信息,请参阅 Dispose(bool) 方法重载

重要

如果要定义一个使用非托管资源的基类,并且该基类拥有或可能拥有应被处置的子类,则应实现 IDisposable.Dispose 方法,并提供 Dispose 的第二个替代,这将在下一部分中讨论。

IDisposable 和继承层次结构

如果基类的子类应该是一次性的,则必须按如下方式实现 IDisposable。 每当您在任何不是 IDisposable(在 Visual Basic 中为 sealed)的类型上实现 NotInheritable 时,都应使用此模式。

  • 它应提供一个公共的非虚拟 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