safehandle 是一種析構機制,她和析構函數有什麼分別。編程
首先要理解析構函數。析構函數在.net中是沒有順序的,所以你不能假定另外一個對象的析構函數在你以後運行,哪怕它是你的成員!若是你的成員也有析構函數,那麼你能作什麼,什麼不該該作?api
第一,在析構函數運行時,你不該該假設它沒清理資源,而去試圖清理它。合理的作法是它應該本身實現dispose模式,你在dispose(true)段落能夠調用它的dispose()函數.由於dispose是可屢次重入的,所以不會有問題。若是它沒有,那就別去理他,由於它的析構函數會釋放它的非託管資源。這樣的好處是,我能夠分別控制每個實現dispose模式的對象成員的控制時機,而dispose(false)也就是析構函數,可以做爲防護性代碼,幫你把忘記的「非託管」資源清理掉。app
第二,一個實現了dispose的.net對象,外部應該怎樣看它?不該該把它所控制的資源看做非託管資源,既然他實現了dispose,也就是「有自行管理」的,所以只須要在dispose(true)段落調用它的dispose函數,讓其在顯式清理實際時產生效果即可。這是我對dispose模式的理解。dispose模式分兩類資源,一類是託管,一類是非託管。託管和非託管並非純粹站在.net角度出發,而是應該理解爲「本身管理」和「其餘對象管理」的差異。你的對象本身打開文件,就本身負責關閉,而其餘對象,包括成員對象,他們控制的資源就由他們控制,在當前立場上看,都是「受託管的」。函數
第三,你的對象的 dispose 應該被顯式調用(也就是最外層對象)!這個是dispose模式的關鍵,由於析構函數只是負責自身非託管資源的釋放,它沒有參與整套資源管理流程。若是你不顯式調用(或者另外編寫代碼),清理流程是不可能正確執行的。性能
第四。爲什麼析構函數不負責dispose的全部內容,首先是析構順序的不肯定性,而資源管控流程須要順序;其次,析構函數沒有明確的調用時機,而dispose能夠在任意時刻調用。測試
而後是safehandle,safehandle聽說有通過優化,可是它也不會搶先在析構函數階段運行,在我測試中是這種狀況。所以,我不知道優化在那裏了。只是添加了一種模式,比析構函數更舵控制,畢竟是外部獨立的類,並且系統已經針對多種資源提供了恰當的子類,惋惜沒有針對com對象資源。優化
總的來講,最佳實踐是:this
1.若是safehandle子類有的系統資源,如句柄,非託管內存等等,用safehandle 模式。.net
2.若是子成員正確實現了dispose,把它視爲託管資源,在dispose(true)中調用。設計
3.若是子成員沒有實現dispose,可是控制了相關資源,也就是你要負責子對象相關資源的顯式控制,千萬別用析構函數清理,由於析構函數階段,子成員可能已經把它關聯的資源給釋放掉了(未必以正確的方式,只是對資源失去控制力),你沒法再清理。由於GC對任意對象的析構函數調用順序是不肯定的。(這一情況只能要求你在編程階段正確調用dispose,而毫不能遺忘調用,不然就有資源泄漏。com for .net就是如此設計的)
4.通常狀況,dispose(true)負責對象鏈的清理流程,dispose(false)即析構函數,負責自身非託管資源。若是沒有這部分能夠不寫析構函數。
5.dispose的實現方式要規範。最終dispose要顯式調用!
規範的dispose該注意哪些,這裏補充一下:
private bool disposedValue = false; // 要檢測冗餘調用 protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // TODO: 釋放託管狀態(託管對象)。 //這裏應該包含成員對象的dispose //大部分被包裝的資源的處理過程都寫在這裏,好比退出過程:先保存,而後關閉,等等 //safehandle.Dispose(); } // TODO: 釋放未託管的資源(未託管的對象)並在如下內容中替代終結器。 // TODO: 將大型字段設置爲 null。 //這裏是自身管理的一些資源的釋放,好比經過c api調用的一些非託管資源 app = null; disposedValue = true; } } //TODO: 僅當以上 Dispose(bool disposing) 擁有用於釋放未託管資源的代碼時才替代終結器。 ~Excel() { // 請勿更改此代碼。將清理代碼放入以上 Dispose(bool disposing) 中。 Dispose(false); } // 添加此代碼以正確實現可處置模式。 void IDisposable.Dispose() { // 請勿更改此代碼。將清理代碼放入以上 Dispose(bool disposing) 中。 Dispose(true); // TODO: 若是在以上內容中替代了終結器,則取消註釋如下行。 //也就是有析構函數就有下面這行代碼,表示顯式運行dispose時,GC不須要再運行析構函數,提升性能 GC.SuppressFinalize(this); }