by Mickey Williams
通過使用Dispose模式可以適當地釋放非內存資源,比如數據庫連接、Win32 interop組件和操作系統的句柄。你不要指望垃圾收集器能夠立即將資源釋放掉,因爲垃圾收集器是由於管制堆(Managed Heap)的內存緊張時才觸發的。你可以快速消耗掉例如數據庫連接等少量資源,但會給程序的擴展性造成副面影響。在不必要的時候不能實現Dispose模式,因爲它可能會增加系統開銷,而這在很多情況下是可以避免的。
在.NET當中Dispose 模式是由一個IDisposable接口來實現的,它包括一個簡單的方法--Dispose:
interface IDisposable { void Dispose(); } |
最明顯的例子是在一個類裏當類的實例搶佔住一個非管制資源(unmanaged resource)時必須實現IDisposable,比如一個本地數據連接或是操作系統的句柄。
另外,記下一個經常被忽略的應該實現IDisposable接口的例子。當一個類實現IDisposable時,實例的正確用法是當對象不在需要時調用Dispose方法刪除它,因此,在你實現一個類,而該類又包含其他實現IDisposable的類時,必須調用Dispose方法。這通常意味着在該類中你必須實現IDisposable,即使它無法直接處理非管制資源。
以下是一個實現IDisposable接口的典型模式:
public class SlalomRacer: IDisposable { bool _disposed = false; public bool IsDisposed { get { return _disposed; } set { _disposed = value; } } ~SlalomRacer() { InternalDispose(false); } public void Dispose() { InternalDispose(true); } protected void InternalDispose(bool disposing) { if(disposing) { GC.SuppressFinalize(this); _managedThing.Dispose(); } _unmanagedThing.Dispose(); } [...] } |
在前面的代碼片斷中,當IDisposable被實現時,可以通過兩種方法調用disposal代碼。首先,如果你直接調用Dispose方法,所有管制和非管制對象均會被列爲被清除目標。可以看到終止操作會執行一個阻止對象被清除掉的優化的步驟。還注意到可以安全地多次調用Dispose方法。調用dispose方法之後,會使用一個標誌來確保這個對象上的任何一個方法都不能被調用,示例代碼如下:
public void SeekHotTub() { if(IsDisposed) throw new ObjectDisposedException("BT"); } |
ObjectDisposedException會提醒你前面已經使用了一個disposed對象。在一個使用過disposed對象上調用其他方法時引發異常是完全有必要的--畢竟,你不能再次使用這些disposed對象。
其次,如果你不調用這個Dispose方法,終止操作會自己調用Dispose(false),它會採用一個和前段代碼稍有不同的代碼路徑。第一,不清除那些管制對象,即使他們也實現了IDisposable接口。你無法確定對象引用是有效的--這些對象可能在等待操作的終止,或者已經被終止了。第二,也沒有必要去調用GC.SuppressFinalization,因爲這些對象已被終止使用了。
最後,如果你在使用C#,你應該利用其語言固有的對IDisposable接口的支持來實現對象清除,你可以使用以下聲明:
using(SlalomRacer mickey = new SlalomRacer()) { // use mickey here mickey.RunGates(); mickey.GetStitches(); } // mickey disposed automatically here |
C#編輯器會適當地發出調用Dispose方法的IL代碼,即使會引發異常。