类实例经常封装对不受运行库管理的资源(如窗口句柄 (HWND)、数据库连接等)的控制。因此,应该既提供显式方法也提供隐式方法来释放这些资源。通过在对象上实现受保护的 Finalize(在 C# 和 C++ 中为析构函数语法)可提供隐式控制。当不再有任何有效的对象引用后,垃圾回收器在某个时间调用此方法。
在有些情况下,您可能想为使用该对象的程序员提供显式释放这些外部资源的能力,以便在垃圾回收器释放该对象之前释放这些资源。当外部资源稀少或者昂贵时,如果程序员在资源不再使用时显式释放它们,则可以获得更好的性能。若要提供显式控制,需实现 IDisposable 提供的 Dispose。在完成使用该对象的操作时,该对象的使用者应调用此方法。即使对对象的其他引用是活动的,也可以调用 Dispose。
注意,即使在通过 Dispose 提供显式控制时,也应该使用 Finalize 方法提供隐式清理。Finalize 提供了候补手段,可防止在程序员未能调用 Dispose 时造成资源永久泄漏。
有关如何实现 Finalize 和 Dispose 以清理非托管理资源的更多信息,请参见垃圾回收。下面的代码示例演示实现 Dispose 的基本设计方案。此示例需要 System 命名空间。
' Design pattern for a base class.
Public Class Base
Implements IDisposable
' Field to handle multiple calls to Dispose gracefully.
Dim disposed as Boolean = false
' Implement IDisposable.
Public Overloads Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overloads Overridable Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
' Free other state (managed objects).
disposed = True
End If
End If
' Free your own state (unmanaged objects).
' Set large fields to null.
End Sub
Protected Overrides Sub Finalize()
' Simply call Dispose(False).
Dispose (False)
End Sub
End Class
' Design pattern for a derived class.
Public Class Derived
Inherits Base
' Field to handle multiple calls to Dispose gracefully.
Dim disposed as Boolean = false
Protected Overloads Overrides Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
' Release managed resources.
disposed = True
End If
End If
' Release unmanaged resources.
' Set large fields to null.
' Call Dispose on your base class.
Mybase.Dispose(disposing)
End Sub
' The derived class does not have a Finalize method
' or a Dispose method without parameters because it inherits
' them from the base class.
End Class
有关阐释 Finalize 和 Dispose 的实现设计方案的更详细的代码示例,请参见实现 Dispose 方法。
自定义释放方法名称
有时特定于域的名称比 Dispose 更合适。例如,文件封装可能需要使用 Close 方法名称。在此情况下,可以通过专用方式实现 Dispose 并创建调用 Dispose 的公共 Close 方法。下面的代码示例阐释了这种模式。可以用适合您的域的方法名称替换 Close。此示例需要 System 命名空间。
' Do not make this method overridable.
' A derived class should not be allowed
' to override this method.
Public Sub Close()
' Call the Dispose method with no parameters.
Dispose()
End Sub
Finalize
下面的规则概括了 Finalize 方法的使用准则:
-
仅在要求终结的对象上实现 Finalize。存在与 Finalize 方法相关的性能开销。
-
如果需要 Finalize 方法,应考虑实现 IDisposable,以使类的用户可以避免因调用 Finalize 方法而带来的开销。
-
不要提高 Finalize 方法的可见性。该方法的可见性应该是 protected,而不是 public。
-
对象的 Finalize 方法应该释放该对象拥有的所有外部资源。此外,Finalize 方法应该仅释放由该对象控制的资源。Finalize 方法不应该引用任何其他对象。
-
不要对不是对象的基类的对象直接调用 Finalize 方法。在 C# 编程语言中,这不是有效的操作。
-
应在对象的 Finalize 方法中调用基类的 Finalize 方法。
表 2 说明: 基类的 Finalize 方法通过 C# 和 C++ 析构函数语法自动进行调用。
释放
下面的规则概括了 Dispose 方法的使用准则:
-
在封装明确需要释放的资源的类型上实现释放设计方案。用户可以通过调用公共 Dispose 方法释放外部资源。
-
在通常包含控制资源的派生类型的基类型上实现释放设计方案,即使基类型并不需要也如此。如果基类型有 Close 方法,这通常指示需要实现 Dispose。在这类情况下,不要在基类型上实现 Finalize 方法。应该在任何引入需要清理的资源的派生类型中实现 Finalize。
-
使用类型的 Dispose 方法释放该类型所拥有的所有可释放资源。
-
对实例调用了 Dispose 后,应通过调用 GC.SuppressFinalize 禁止 Finalize 方法运行。此规则的一个例外是当必须用 Finalize 完成 Dispose 没有完成的工作的情况,但这种情况很少见。
-
如果基类实现了 IDisposable,则应调用基类的 Dispose 方法。
-
不要假定 Dispose 将被调用。如果 Dispose 未被调用,也应该使用 Finalize 方法释放类型所拥有的非托管资源。
-
当资源已经释放时,在该类型上从实例方法(非 Dispose)引发一个 ObjectDisposedException。该规则不适用于 Dispose 方法,该方法应该可以在不引发异常的情况下被多次调用。
-
通过基类型的层次结构传播对 Dispose 的调用。Dispose 方法应释放由此对象以及此对象所拥有的任何对象所控制的所有资源。例如,可以创建一个类似 TextReader 的对象来控制 Stream 和 Encoding,两者均在用户不知道的情况下由 TextReader 创建。另外,Stream 和 Encoding 都可以获取外部资源。当对 TextReader 调用 Dispose 方法时,TextReader 应继而对 Stream 和 Encoding 调用 Dispose,使它们释放其外部资源。
-
考虑在调用了某对象的 Dispose 方法后禁止对该对象的使用。重新创建已释放的对象是难以实现的方案。
-
允许 Dispose 方法被调用多次而不引发异常。此方法在首次调用后应该什么也不做。