2010-03-30 58 views
28

我不能相信我仍然對此感到困惑,但是,以任何方式,讓我們終於明白它:我是否需要在託管對象上調用Dispose()?

我有一個類覆蓋了OnPaint做一些繪圖。爲了加快速度,我在構造函數中創建了筆,畫筆等,以便OnPaint不需要繼續創建和處理它們。

現在,我要確保我總是處置這類物品,但我有一種感覺,我不需要,因爲,儘管他們實現IDisposable,他們管理的對象。

這是正確的嗎?


感謝所有的答案,這個問題已經確定了。
我很高興我一直在使用'使用'一直保持警惕,所以我不需要通過我所有的代碼檢查。我只是想清楚,我不是一個毫無意義的用戶。另外,我確實有一個奇怪的情況,最近,我不得不更換一個使用塊並手動調用dispose!我會挖掘出來,並創建一個新的問題。

+0

能否請您爲具有明確調用Dispose替換「使用」塊提供詳情(或鏈接)?我很好奇看到那裏發生了什麼。 – 2016-10-14 14:47:33

回答

43

它是而不是正確。您需要處理實現IDisposable的對象。這就是他們實施IDisposable的原因 - 指定它們(直接或間接)包裝非託管資源的事實。

在這種情況下,非託管資源是GDI手柄,如果你無法處理它們時,你實際上是與他們做,你會泄漏的句柄。現在這些特定對象有終結器這將導致資源在GC啓動時被釋放,但您無法知道什麼時候會發生。從現在開始可能是10秒,可能是從現在開始的10天;如果您的應用程序沒有產生足夠的內存壓力導致GC啓動並在這些畫筆/筆/字體等上運行終結器,那麼在GC意識到發生了什麼之前,最終可能導致GDI資源的操作系統匱乏。

此外,您不能保證每個非託管包裝實際上都實現了終結器。 .NET框架本身是在類實現IDisposablecorrect pattern實現它的意義相當一致的,但它是完全可能的一些其他類有一個破碎的實現,不包括終結,因此不收拾妥當除非明確調用Dispose。一般來說,IDisposable的目的是你不應該知道或關心具體的實現細節;相反,如果它是一次性的,那麼你就可以把它處置掉。

道德故事:總是處置IDisposable對象。如果您的課程「擁有」IDisposable的對象,則它應該自行實施IDisposable

+0

我可能會將終結器稱爲「理想」模式,但在很多情況下,IDisposable對象不能有效地實現終結器,例如,他們在哪裏使用線程靜態變量或來自未知對象的事件訂閱。終結器在與其他代碼不同的線程上下文中運行,這將很難訪問與在另一個線程上創建的廢棄IDisposable關聯的線程靜態變量。雖然可以設計終結器將事件解除掛鉤,但大多數事件在這種情況下無法安全地解除掛鉤。 – supercat 2011-10-18 13:11:05

+1

@supercat:這是一個非常危險的設計,將非託管資源存儲在線程本地存儲中。這足以說明,終結器可能會運行在不同的線程中,但是如何保證'Dispose'將從導致資源初始化的相同線程中調用?我可以想到幾種情況,情況並非如此。老實說,不要使用ThreadStatic來處理這種事情,請將關鍵資源保存在可追蹤它們的地方。 – Aaronaught 2011-10-19 00:44:39

+0

線程本地存儲中的項目往往是*本身*,非託管資源;如果創建它們的代碼在不清除它們的情況下放棄控制權,但該線程仍然存在(可能在線程池中),則這些項目可能永遠無法清理。使用IDisposable進行這些操作可以使用「使用」塊來管理它們,這些塊本質上會正確處理線程。 – supercat 2011-10-19 04:20:59

9

你需要處理它們。

管理對象由自己管理自己內存。但內存不是對象使用的唯一資源。 Dispose()旨在釋放其他資源。

1

不,IDisposable用於使用非託管資源的託管對象。通常情況下,你應該總是在完成後處置它們。

1

你真的需要來查找文檔中的畫筆,鋼筆等

如果他們不使用非託管資源,您可能沒有調用Dispose。但是使用/配置模式有時會被「誤用」。作爲一個例子,考慮一下ASP.NET MVC框架。在這裏,你可以寫這樣的:

using(Html.BeginForm(...)){ 
    ///HTML input fields etc. 
} 

Html.BeginForm(...)被調用,一個FORM標籤將被輸出。當使用語句結束時,將從Html.BeginForm(...)返回的對象上調用Dispose。調用Dispose會導致結束FORM標記被渲染。通過這種方式,編譯器實際上會強制執行FORM標籤的配對,所以您不會忘記結束標籤。

2

你是否對此進行了分析,看是否創建&處置這些對象確實是一個問題?我不認爲這是。

通過使用「創建使用塊」模式,您可以讓事情變得更輕鬆,並且肯定不會出錯。

如果您確實想要創建它們一次,那麼還要在您擁有的類上實現IDisposable,並將Dispose重複用於您擁有的對象。不需要析構函數(終結器)。

這樣做對於實際上不需要處理的對象幾乎沒有成本處置,但是如果您忘記在需要處理的對象上丟棄Dispose,則會產生很大的成本。

1

不,Pen s和Brush es是不是完全託管對象。

它們包含非託管資源的句柄,即底層圖形系統中相應的GDI對象。 (不確定這裏的確切術語......)

如果你不處理它們,句柄將不會被釋放,直到對象被垃圾回收器完成,並且不能保證它很快就會發生,或者根本沒有。

3

我寫了一個GDI +圖表組件,它使用了大量的筆和畫筆。我創建了它們,並將它們放置在執行繪製的代碼塊中,並且性能從來都不是問題。更好的是,然後有一個長壽的手柄掛在操作系統恕我直言。

1

沒有錯。我同意Aaronaught。另外,微軟建議,在Don Box發佈的2003年中期網絡廣播中,每個.Net開發者都應該處理他們自己的對象,無論是託管的還是非託管的,因爲這可以將代碼性能提高20%。如果做得對,它可以大大提高性能。因此,它是每個.NET開發人員都需要知道和使用的核心技能。

5

你的方法有一個明顯的諷刺。通過預先創建筆/畫筆,您正是創建 Dispose()試圖解決的問題。那些GDI對象將會更長,就像您不調用Dispose()時那樣。實際上更糟糕的是,他們至少會在表格關閉之前。

他們可能在足夠長的時間才能晉升到第二代。垃圾收集器不會經常執行第2代收集,現在是更多對他們調用Dispose()很重要。通過將表單的Dispose()方法從Designer.cs文件移動到form.cs文件並添加Dispose調用來完成此操作。

但是,這樣做是正確的。鋼筆和畫筆是非常便宜的物品。在Paint事件中創建它們。然後使用using語句,以便它們立即得到處理。使用秒錶類重新確保這實際上不會導致任何放緩。

1

其他人提到「使用」爲GDI對象塊 - 在這裏是一個代碼示例:

using(var bm = new Bitmap()) 
using(var brush = new Brush()) 
{ 

    // code that uses the GDI objects goes here 
    ... 

} // objects are automatically disposed here, even if there's an exception 

注意,只要你想爲一個單一的代碼塊,你可以有許多「使用」線。

我認爲這是一個很好的,乾淨的方式來處理一次性對象。

0

雖然你問筆和筆刷,字體是一個奇怪的怪類。特別是,如果爲設置控件的Font屬性創建字體,則仍然需要負責處理該字體 - 所有權不會轉移到控件 - 但可以通過將字體置於任何時候 - 即使在字體創建完成之後,即可將其分配給控件。看起來Font是託管信息對象和非託管GDI資源的組合,出於某種目的只有前者是必需的。奇怪的設計 - 字體應該是兩個類。

相關問題