在.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