本文提供了此 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
语句(use
F# 中的表达式)实际上是语法便利。 在编译时,语言编译器为 try
/finally
块实现中间语言(IL)。
有关 using
语句的更多信息,请参阅 Using 语句或使用 Statement 主题。
Try/Finally 块
如果您的编程语言不支持像 C# 或 Visual Basic 中的using
语句,或 F# 中的use
语句这样的构造,或者您不想使用它,则可以从IDisposable.Disposefinally
try
语句块中调用/实现。 以下示例将上一示例中的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-finally、try...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