C 中標準Dispose模式的實現

2022-08-28 08:51:12 字數 2771 閱讀 7496

摘要:c#程式中的dispose方法,一旦被呼叫了該方法的物件,雖然還沒有垃圾**,但實際上已經不能再使用了。所以使用上要仔細考慮細節。

需要明確一下c#程式(或者說.net)中的資源。簡單的說來,c#中的每乙個型別都代表一種資源,而資源又分為兩類:

託管資源:由clr管理分配和釋放的資源,即由clr裡new出來的物件;

非託管資源:不受clr管理的物件,windows核心物件,如檔案、資料庫連線、套接字、com物件等;

毫無例外地,如果我們的型別使用到了非託管資源,或者需要顯式釋放的託管資源,那麼,就需要讓型別繼承介面idisposable。這相當於是告訴呼叫者,該型別是需要顯式釋放資源的,你需要呼叫我的dispose方法。

不過,這一切並不這麼簡單,乙個標準的繼承了idisposable介面的型別應該像下面這樣去實現。這種實現我們稱之為dispose模式:

public class sampleclass : idisposable

///

/// 不是必要的,提供乙個close方法僅僅是為了更符合其他語言(如c++)的規範

///

public void close()

///

/// 必須,以備程式設計師忘記了顯式呼叫dispose方法

///

~sampleclass()

///

/// 非密封類修飾用protected virtual

/// 密封類修飾用private

///

///

protected virtual void dispose(bool disposing)

if (disposing)

}// 清理非託管資源

if (nativeresource != intptr.zero)

//讓型別知道自己已經被釋放

disposed = true;

}public void samplepublicmethod()

//省略}}

在dispose模式中,幾乎每一行都有特殊的含義。

在標準的dispose模式中,我們注意到乙個以~開頭的方法:

///

/// 必須,以備程式設計師忘記了顯式呼叫dispose方法

///

~sampleclass()

這個方法叫做型別的終結器。提供終結器的全部意義在於:我們不能奢望型別的呼叫者肯定會主動呼叫dispose方法,基於終結器會被垃圾**器呼叫這個特點,終結器被用做資源釋放的補救措施。

乙個型別的dispose方法應該允許被多次呼叫而不拋異常。鑑於這個原因,型別內部維護了乙個私有的布林型變數disposed:

private bool disposed = false;

在實際處理**清理的方法中,加入了如下的判斷語句:

if (disposed)

//省略清理部分的**,並在方法的最後為disposed賦值為true

disposed = true;

這意味著型別如果被清理過一次,則清理工作將不再進行。

應該注意到:在標準的dispose模式中,真正實現idisposable介面的dispose方法,並沒有實際的清理工作,它實際呼叫的是下面這個帶布林引數的受保護的虛方法:

///

/// 非密封類修飾用protected virtual

/// 密封類修飾用private

///

///

protected virtual void dispose(bool disposing)

之所以提供這樣乙個受保護的虛方法,是為了考慮到這個型別會被其他類繼承的情況。如果型別存在乙個子類,子類也許會實現自己的dispose模式。受保護的虛方法用來提醒子類必須在實現自己的清理方法的時候注意到父類的清理工作,即子類需要在自己的釋放方法中呼叫base.dispose方法。

還有,我們應該已經注意到了真正撰寫資源釋放**的那個虛方法是帶有乙個布林引數的。之所以提供這個引數,是因為我們在資源釋放時要區別對待託管資源和非託管資源。

在供呼叫者呼叫的顯式釋放資源的無參dispose方法中,呼叫引數是true:

public void dispose()

這表明,這個時候**要同時處理託管資源和非託管資源。

在供垃圾**器呼叫的隱式清理資源的終結器中,呼叫引數是false:

~sampleclass()

這表明,隱式清理時,只要處理非託管資源就可以了。

那麼,為什麼要區別對待託管資源和非託管資源。在認真闡述這個問題之前,我們需要首先弄明白:託管資源需要手動清理嗎?不妨先將c#中的型別分為兩類,一類繼承了idisposable介面,一類則沒有繼承。前者,我們暫時稱之為非普通型別,後者我們稱之為普通型別。

非普通型別因為包含非託管資源,所以它需要繼承idisposable介面,但是,這個包含非託管資源的型別本身,它是乙個託管資源。所以說,託管資源需要手動清理嗎?這個問題的答案是:託管資源中的普通型別,不需要手動清理,而非普通型別,是需要手動清理的(即呼叫dispose方法)。

dispose模式設計的思路基於:如果呼叫者顯式呼叫了dispose方法,那麼型別就該按部就班為自己的所以資源全部釋放掉。如果呼叫者忘記呼叫dispose方法,那麼型別就假定自己的所有託管資源(哪怕是那些上段中闡述的非普通型別)全部交給垃圾**器去**,而不進行手工清理。理解了這一點,我們就理解了為什麼dispose方法中,虛方法傳入的引數是true,而終結器中,虛方法傳入的引數是true。

注意:我們提到了需要及時釋放資源,卻並沒有進一步細說是否需要及時讓引用等於null這一點。有一些人認為等於null可以幫助垃圾**機制早點發現並標識物件是垃圾。其他人則認為這沒有任何幫助。

C 的Dispose模式複習

using system using system.collections.generic using system.text namespace perfectfinalizedispose 這裡實現了idispose中的 dispose方法 public void dispose 在這裡做實際的...

如何實現標準的dispose

前面的文章我們說過,如果物件包含非託管資源那麼就必須要正確的清理,現在我們就來說一下如何清理。針對非託管資源 net 會採用一套標準的模式來完成清理工作。也就是說如果開發人員自己編寫的類中存在非託管資源,那麼這個類的使用者就會認為這個類遵循 net 的垃圾清理模式。標準的 dispose 模式即實現...

C 中標準庫std string的實現

以下實現了c 標準模板庫中std string中的部分實現,參考了cplusplus.關於c 中標準模板庫std string的介紹和用法可以參考 實現 string.hpp如下 ifndef fbc stl string hpp define fbc stl string hpp include ...