在.net托管平台,对于大多数对象,都可以依赖于 .NET 垃圾回收器来进行回收,但是对于非托管对象却需要你手动去回收,以免造成资源泄漏从而引发程序崩溃问题,最常见的非托管资源类型是包含系统资源的对象,如文件、窗口、网络连接或数据库连接。虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但无法了解如何发布并清理这些非托管资源。
想要显示的释放非托管资源,我们需要将非托管资源的封装类实现IDisposable接口,在Dispose方法之中编写代码启用非托管资源的确定性释放。
在这里引用微软的官方示例:
using System;
using System.ComponentModel;
// The following example demonstrates how to create
// a resource class that implements the IDisposable interface
// and the IDisposable.Dispose method.
public class DisposeExample
{
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.
public class MyResource: IDisposable
{
// Pointer to an external unmanaged resource.
private IntPtr handle;
// Other managed resource this class uses.
private Component component = new Component();
// Track whether Dispose has been called.
private bool disposed = false;
// The class constructor.
public MyResource(IntPtr handle)
{
this.handle = handle;
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if(!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if(disposing)
{
// Dispose managed resources.
component.Dispose();
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
CloseHandle(handle);
handle = IntPtr.Zero;
// Note disposing has been done.
disposed = true;
}
}
// Use interop to call the method necessary
// to clean up the unmanaged resource.
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~MyResource()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
}
public static void Main()
{
// Insert code here to create
// and use the MyResource object.
}
}
使用此方法可关闭或释放由实现该接口的类的实例所持有的非托管资源,如文件、流和句柄。 按照约定,此方法用于与释放对象占用的资源或准备要重用的对象相关联的所有任务。
注意,当多个封装了非托管资源的类存在封装关系时,要注意必须依次执行类对象的Dispose方法,比如说A封装了B,B封装了C,那么A的Dispose则必须执行B成员的Dispose,而B的Dispose则必须执行C成员的Dispose,无论存在多少层的封装关系,都要如此。
对于拥有可释放子类的基类,无论如何都必须要实现IDisposable接口。只要类型实现了IDisposable接口,并且非以sealed关键字进行修饰的密封类,则必须要以如下模式进行定义相关方法:
-
它应提供一个公共、非虚 Dispose() 方法和一个受保护的虚拟
Dispose(Boolean disposing)
方法。 -
Dispose() 方法必须调用
Dispose(true)
并且应取消对性能的终止。 -
基类型不应包括任何终结器,即析构函数。
下面的代码段反映了基类的释放模式。 它假定您的类型不重写 Object.Finalize 方法。
引用微软官方示例:
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
class BaseClass : IDisposable
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Instantiate a SafeHandle instance.
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing) {
handle.Dispose();
// Free any other managed objects here.
//
}
disposed = true;
}
}
如果确实要重写 Object.Finalize 方法,你的类应实现以下模式。
using System;
class BaseClass : IDisposable
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(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;
}
~BaseClass()
{
Dispose(false);
}
}
子类应实现以下可释放模式:
-
它们必须重写
Dispose(Boolean)
并调用基类Dispose(Boolean)
实现。 -
如果需要,他们可以提供终结器。 终结器必须调用
Dispose(false)
。
请注意,派生类本身并不实现 IDisposable 接口,并且不包含无参数的 Dispose 方法。 它们只会重写基类 Dispose(Boolean)
方法。
下面的代码段反映派生类的释放模式。 它假定您的类型不重写 Object.Finalize 方法。
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
class DerivedClass : BaseClass
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Instantiate a SafeHandle instance.
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing) {
handle.Dispose();
// Free any other managed objects here.
//
}
// Free any unmanaged objects here.
//
disposed = true;
// Call base class implementation.
base.Dispose(disposing);
}
}
本篇博文参考微软官方文档IDisposable