本文簡述了 c# 中 dispose 模式的一些知識之前對 c# 中的 dispose 模式只有些模糊印象,近來又了解了一些相關知識,在此簡單做些記錄~
c# 程式中每種型別都可以看做是一種資源,這些資源可以分成兩類:
託管資源 : 受 clr 管理(分配和釋放)的資源,譬如 new 出的某個型別物件
非託管資源 : 不受 clr 管理(分配和釋放)的資源,譬如某個 native 的檔案控制代碼
對於託管資源,由於受 clr 的管理,大部分情況下我們都不用操心資源的釋放問題,但是對於非託管資源,由於不受 clr 的管理,釋放的問題便必須我們自己來做了.
那麼我們通過什麼方法來釋放這些非託管資源呢, c# 提供了乙個標準介面idisposable:
public inte***ce idisposable
如果你程式中的某個型別需要釋放非託管資源,就讓他實現idisposable介面,也就是通過void dispose()方法來實現非託管資源的釋放, 示例**如下:
// dispose pattern v1
public class disposepattern : idisposable
public void dispose()
} // close handle method
[system.runtime.interopservices.dllimport("kernel32")]
extern static bool closehandle(intptr handle);
}
上面的示例**有個很大的問題,如果外部**沒有呼叫 disposepattern 的 dispose 方法,那麼 disposepattern 持有的非託管資源(m_handle)便洩露了.
就程式設計規範來講,其實是應該規避外部**不呼叫 dispose 方法的行為,如果這可以做到,那麼示例**中的 dispose 實現便已經足夠了,但是這在實際中往往難以保證(或者說做到保證的成本太高),另外從實現的角度來看, disposepattern 如果能在外部**不呼叫 dispose 方法的前提下仍然保證非託管資源不洩露,那麼程式也會更加健壯.
如何實現呢?我們需要借助 c# 中的析構函式(或者叫終結器)
這裡我們暫時不去關注 c# 中析構函式的各個細節,只要知道析構函式可以在型別被**之前執行就行了,新的示例**如下:
// dispose pattern v2
public class disposepattern : idisposable
// destructor
~disposepattern() }
public void dispose()
// get rid of ~disposepattern() call
gc.suppressfinalize(this); }
// close handle method
[system.runtime.interopservices.dllimport("kernel32")]
extern static bool closehandle(intptr handle);
}
可以看到我們額外定義了~disposepattern(),並在其中實現了非託管資源的釋放,這就保證了即使外部**不呼叫 dispose 方法,非託管資源也能正確釋放(在 disposepattern **之前),相對的,如果外部**呼叫了 dispose 方法,我們便不需要再呼叫 ~disposepattern() 了(當然,這裡只是說不需要,不是說不可以,這裡在 dispose 之後繼續呼叫 ~disposepattern() 也是可以的,這也是出於健壯性的考慮), dispose() 方法中的gc.suppressfinalize(this);便是用來"遮蔽"析構函式的執行的(定義了析構函式的型別可以通過呼叫 gc.suppressfinalize 來抑制析構函式的執行).
實際的**中,乙個型別除了持有非託管資源,自然也會持有託管資源,如果這些託管資源(型別)也實現了idisposable介面(或者更廣義的來說,實現了 dispose 之類的釋放資源方法.這裡我們將問題標準化(簡化),規定實現釋放資源方法就需要實現idisposable介面)
最終的實現**如下:
// dispose pattern v3
public class disposepattern : idisposable
// release external unmanaged resource
if (m_handle != intptr.zero)
m_disposed = true;
} }public disposepattern(intptr handle)
// destructor
~disposepattern()
public void dispose()
// close handle method
[system.runtime.interopservices.dllimport("kernel32")]
extern static bool closehandle(intptr handle);
}
上面示例中的 disposepattern 實現便是所謂的dispose 模式,**中的幾個要點還需要細細說明一下:
我們抽象了乙個內部的void dispose(bool disposing)方法來輔助我們的 dispose 模式實現,這主要是出於**可讀性和可維護性的考慮.
新增加的bool m_disposed成員主要是為了解決外部**重複呼叫(之前說明的是不呼叫和僅呼叫一次) dispose() 方法的問題(之前其實也存在重複呼叫的問題,只是我們通過if (m_handle != intptr.zero)這種編碼方式規避了)
void dispose(bool disposing) 方法的引數bool disposing的意思,是用來區分 dispose 呼叫路徑的(是外部**呼叫還是析構函式呼叫),如果是外部**呼叫,我們一併釋放託管資源和非託管資源,如果是析構函式呼叫,我們僅釋放非託管資源(託管資源在他們各自的析構函式中進行 dispose),至於為何需要做這種區分,可以簡單理解為這是 dispose 模式的實現規範(想繼續了解的同學可以進一步看看後面的說明).
參考資料
改善c#程式的建議4:c#中標準dispose模式的實現
idisposable inte***ce
why using finalizers is a bad idea
更多說明
之前我們提到, dispose 模式中區分了 dispose 的呼叫路徑(如果是外部**呼叫,我們一併釋放託管資源和非託管資源,如果是析構函式呼叫,我們僅釋放非託管資源),這裡可以引出幾個問題:
如果是外部**呼叫,我們可以不釋放託管資源嗎(標準實現是一併釋放託管資源和非託管資源)?
如果是析構函式呼叫,我們可以釋放託管資源嗎(標準實現是僅釋放非託管資源)?
程式設計小知識之 Dithering
本文簡單介紹了 dithering 抖動 的一些知識 圖形後處理有一種操作稱為 dithering 抖動 所謂 dithering,就是一種能夠在較小色彩空間上 模擬出 較大色彩空間的影象處理方法,說的有些抽象,我們來舉個例子 假設我們需要在顯示器上顯示以下 來自這裡 的畫素格式為 rgb 24rg...
網路程式設計小知識
pdu 協議資料單元,有隱藏size上限,如果應用程式的包超過指定上限會被劃分為多個pdu傳送 tcp不提供記錄結束標記,需要應用程式自己提供,比如http的 r n 編寫tcp協議需要注意ipv4和ipv6的相容性,可以在應用程式中實現協議無關性。getaddrinfo 多執行緒的socket程式...
程式設計小知識之 Lua 長度運算子
本文講解了 lua 中長度運算子 的一些知識 注 以下討論基於 lua 5.3.5 版本 基礎 lua 中的長度運算子 可以用於獲取 table 的 長度 舉個簡單的例子 local t print t 3但其實對於 table 而言,長度運算子並不等同於獲取 table 的 長度 更準確一些的說法...