C 中IDispose介面的實現及為何這麼實現詳解

2022-10-06 06:27:08 字數 3626 閱讀 9810

前言

我原本認為對於idispose的實現方法,只要在裡面釋放非託管資源就行了,但是通過網上資料,看到很多實現方法並不是僅僅做釋放非託管資源,非常迷惑,關鍵是這些資料也沒詳細的告訴你為什麼這麼做?之後通過stackoverflow了解到這一步一步的原因,說的十分詳細,結合自己的認識,翻譯後分享給大家:

一、idispose的實現方法

具體的實現方法,你可以直接檢視這個我們**的教程:

如果你能看懂,並且很清楚為什麼那www.cppcns.com麼做。那麼以下的文章你就可以略去不看。如果不清楚為什麼那麼做,請帶著你的迷惑往下看:

二、為什麼那樣實現

英文好的可以直接去stackoverflowwww.cppcns.com原文位址:

程式設計客棧se-of-the-idisposable-inte***ce/538238#538238

2.1、進行之前

在c++中,所有你在堆上申請的記憶體空間,必須手動釋放掉,否則就會造成記憶體的洩露。這可能會讓你在寫程式的時候要花點心思在記憶體的管理上而不是專注於解決你程式設計的目的—解決問題。所以作為c++的進化版c#使用了gc(garbage collector)來進行記憶體的管理以達到自動釋放不需要的記憶體的目的,但是gc並不能做的十分完美,對於一些非託管資源,gc無能為力,這就要求我們必須手動的釋放那麼非託管資源,為了更好的去做到這一點,我們就要編寫一種方法,通過手動呼叫這個方法,我們就能夠釋放掉非託管資源。

注:什麼是託管資源和非託管資源?

託管資源就是託管給clr的資源,clr能對這些資源進行管理。而非託管資源則是clr無法對這些資源管理,這些資源的申請、釋放必須由使用者自行管理。

例如,像win32程式設計中的檔案控制代碼,上下文控制代碼、視窗或網路連線等資源都屬於非託管資源。但是如果這些非託管資源在.net中進行了封裝,成為了.net類庫中的一部分,它就不屬於非託管資源了,因為在對它們封裝的過程中,就實現了它們的自動管理功能。

也就是說,你能在.net中找到的類產生的物件,都是託管資源。

(理解這點很重要,這可能是你看不懂上面實現教程的重要乙個原因!)

注:gc進行垃圾**的時間和順序?

gc進行垃圾**的時間我們根本無法確定(當然你手動呼叫gc的垃圾**方法除外),並且順序也不能確定!也就是說,你先申請的空間有可能在你後申請的空間釋放之後釋放。

gc對於實現析構函式和沒實現析構函式的類處理方法不一樣,簡單些說gc對於實現了析構函式的類一定會呼叫他們的析構函式。

關於.net的垃圾**機制,你可以暫時先知道這麼多,待看完了這篇文章再去深入了解。

2.2、我們需要編寫一種方法去釋放!

為了去清除一些非託管資源,你建立的類需要有乙個public方法,方法的名字可以隨意命名

例如:public void cleanup()

public void shutdown()

……你可以這麼做,但是有乙個標準的名字

public void dispose()

甚至有乙個介面idisposeable,裡面包含的就是剛才那個方法

public inte***ce idisposable

因此最好的辦法是讓你的類去實現idisposable介面,在介面內的dispose方法內提供一段清除非託管資源的**。

public void dispose()

ok。這就完成了,除非你想做的更好!

2.3、別忘了類中的託管資源還佔著空間!

託管資源佔著空間?你首先想到的可能是那些int,string等等這些託管資源,它們能占用幾個空間,他們佔著就佔著唄!

但是託管資源可不僅僅是那些資源,要是你的物件使用了250mb的system.drawing,bitmap(這是在.net frame中的,屬於託管資源)作為一些緩衝怎麼辦?當然,你知道這是乙個.net的託管資源,所以gc理所應當的將會釋放它。但是你真的想留著250mb的記憶體空間就那麼被占用著?然後等待著gc最終釋放它?更或者要是有乙個更大資料庫連線呢?我們當然不想讓那連線白白占用來等待gc的終結!

如果使用者呼叫了dispose方法(意味著他們不再想使用這個物件裡的一切)為什麼不去扔掉那些浪費空間的點陣圖資源和資料庫連線呢?

那麼,我們就應該這麼做:

所以,讓我們更新我們的dispose方法來釋放那些託管資源

public void dispose()

if (this.framebufferimage !=null)

}ok,做的很好了,除非你想做的更好!

2.4、總會有人粗心忘記呼叫dispose!

要是有人使用你的類建立了物件,但是忘記呼叫dispose方法該怎麼辦?這將會洩露一些非託管的資源!

注意:忘記呼叫dispose方法雖然會造成非託管資源的洩露,但是對於那些託管資源來說,是不會洩露的,因為最終gc會行動起來,在後台執行緒中釋放那些和託管資源有關的記憶體空間。這包括你建立的物件和其中的託管資源(例如bitmap和資料庫連線) (為什麼?往下看)

也就是說,如果你忘記呼叫dispose方法,這個類應該自動進行一些補救措施!我們可以想到設計一種方法來做為一種後備方法:利用gc最終呼叫的終結器

注意:gc最終雖然會釋放託管資源,但是gc並不知道或者關心你的dispose方法。那僅僅是乙個我們選擇的名字。

gc呼叫析構函式是乙個完美的時機來釋放託管資源,我們實現析構函式的功能通過重寫finalize方法。

注意:在c#中,你不能真的去使用重寫虛函式的方法去重寫finalize方法。你只能使用像c++的析構函式的語法去編寫,編譯器會自動對你的**進行一些改動來實現override終結器方法。(具體可檢視msdn中析構函式一節)

~myobject()

if (this.framebufferimage !=null) } }

這樣idisposable中的dispose方法就變成了這樣:

public void dispose()

終結器就變成了這樣…

~myobject()

注意:你的類如果是從另乙個類繼承而來,那麼你不要忘記去呼叫父類的dispose方法

public dispose()

finally

}所有的一切看起來都已經很好了,除非,你想做的更好!

2.5、最完美的方法?

如果使用者手動呼叫了dispose方法,這樣,一切都被清空了。之後呢,因為你重寫了finalize方法,gc一定呼叫這個方法,它將會再一次呼叫dispose方法!

這不僅是一種效能上的浪費,而且關鍵是在dispose方法呼叫後,那些引用已經變成了垃圾,gc會呼叫這些垃圾引用!

解決的方法是通過在dispose方法中呼叫gc.suppressfinalize() 來阻止gc去呼叫finalize方法

public void dispose()

這樣,每一件事都照顧到了!

(注:其實可以將dispose(bool disposing)方法變成虛函式,如果你的類被繼承)

至此,我們一步一步實現了最好的idisposable方法,現在回頭去看看一開始的實現idisposable介面教程,是不是一切的透徹了?

三、使用終結器還是dispose方法釋放非託管資源?

其實兩種方法都可以,但是就像在一開始提到的,gc的垃圾**時間不確定,對於那些你已經不需要的資源,還是盡快釋放比較好,不應該總等著gc的垃圾**,而且還有乙個好處是,降低gc垃圾**的時間,提高效率。何樂而不為呢?

總結本文標題: c#中idispose介面的實現及為何這麼實現詳解

本文位址:

C 實現IDispose介面

net的gc機制有兩個問題 首先gc並不能釋放所有資源,它更不能釋放非託管資源。其次,gc也不是實時的,所有gc存在不確定性。為了解決這個問題donet提供了析構函式 public class testclass system.idisposable protected的dispose方法,保證不會...

C 實現IDispose介面

net的gc機制有兩個問題 首先gc並不能釋放所有資源,它更不能釋放非託管資源。其次,gc也不是實時的,所有gc存在不確定性。為了解決這個問題donet提供了析構函式 public class testclass system.idisposable protected的dispose方法,保證不會...

C 介面 介面的實現

c 定義了介面後,就要在子類中實現。c 中通常把子類和父類的關係稱為繼承,子類和介面的關係稱為實現。子類可以繼承乙個父類,可以實現多個介面。介面中不能定義建構函式,所以介面不能例項化。下面我們看例子 using system public inte ce ichoose public class t...