為了制作高性能的靈活的應用程序,用最優化的方式使用你的資源是至關重要的。一個技巧是盡可能晚地使用你的資源並且在使用後盡快釋放它。我在這裡的意圖是描述在C#中的對象清除機制。
解構器
我們知道,‘解構器’被用來清除類的事例。當我們在C#中使用解構器是,我們必須記住以下幾點:
一個類只能有一個解構器。
解構器不能被繼承或重載。
解構器不能被調用。他們是自動被(編譯器)調用的。
解構器不能帶修飾或參數。
下面是類MyClass解構器的一個聲明:
~ MyClass()
// Cleaning up code goes here
}
程序員不能控制解構器何時將被執行因為這是由垃圾收集器決定的。垃圾收集器檢查不在被應用程序使用的對象。它認為這些條件是符合清楚的並且收回它們的內存。解構器也在程序退出時被調用。當解構器執行時其背後所發生的那一幕是解構器隱式調用對象基類的Object.Finalize方法。因此上述解構器代碼被隱含轉化成:
protectedoverridevoid Finalize()
{
try
{
// Cleaning up .
}
finally
{
base.Finalize();
}
}
現在,讓我們看一個解構器怎樣被調用的例子。我們有三個類A,B和C。B派生自A,C派生自B。每個類有它們自己的構造器和解構。在類App的main函數中,我們創建C的對象。
using System;
class A{public A(){ Console.WriteLine("Creating A");}~A(){ Console.WriteLine("Destroying A"); }}class B:A{ public B() { Console.WriteLine("Creating B"); } ~B() { Console.WriteLine("Destroying B"); }} class C:B{ public C() { Console.WriteLine("Creating C"); } ~C() { Console.WriteLine("Destroying C"); }} class App{ publicstaticvoid Main() { C c=new C(); Console.WriteLine("Object Created "); Console.WriteLine("Press enter to Destroy it"); Console.ReadLine(); c=null; //GC.Collect(); Console.Read(); }} 正如我們預料的,基類的構造器將會被執行並且程序會等待用戶按‘enter’。當這個發生,我們把類C的對象置為null.但解構器沒有被執行..!!??正像我們所說的,程序員無法控制解構器何時被執行因為這是由垃圾搜集器決定的。但程序退出時解構器被調用了。你能通過重定向程序的o/p到文本文件來檢查這個。我將它輸出在這裡。注意到基類的解構器被調用了,因為在背後base.Finalize()被調用了。
Creating A
Creating BCreating CObject Created Press enter to Destroy itDestroying CDestroying BDestroying A 所以,如果一旦你使用完對象你就想調用解構器,你該怎麼做?有兩個方法:調用垃圾搜集器來清理。
實現IDisposable的Dispose方法。
調用垃圾搜集器
你能通過調用GC.Collect方法強制垃圾搜集器來清理內存,但在大多數情況下,這應該避免因為它會導致性能問題。在上面的程序中,在GC.Collect()處移除注釋。編譯並運行它。現在,你能看到解構器在控制台中被執行了。
實現IDisposable接口
IDisposable 接口包括僅有的一個公共方法,其聲明為void Dispose()。我們能實現這個方法來關閉或釋放非托管資源如實現了這個接口的類事例所控制的文件,流,和句柄等。這個方法被用做所有任務聯合對象的資源釋放。當實現了這個方法,對象必須尋求確保所有擁有的資源被繼承結構中關聯的資源也釋放(不能把握,翻不出來)。
class MyClass:IDisposable
{publicvoid Dispose() { //implementation }} 當我們實現了IDisposable接口時,我們需要規則來確保Dispose被適當地調用。
聯合使用解構器和IDisposable接口
Public class MyClass:IDisposable
{private bool IsDisposed=false;public void Dispose(){ Dispose(true); GC.SupressFinalize(this);}protected void Dispose(bool Diposing){ if(!IsDisposed) { if(Disposing) { //Clean Up managed resources } //Clean up unmanaged resources } IsDisposed=true;} ~MyClass(){ Dispose(false);}} 在這裡重載了Dispose(bool)來做清理工作,並且所有的清理代碼都僅寫在這個方法中。這個方法被解構器和IDisposable.Dispose()兩著調用。我們應該注意Dispose(bool)沒有在任何地方被調用除了在IDisposable.Dispose()和解構器中。
當一個客戶調用IDisposable.Dispose()時,客戶特意地想要清理托管的和非托管資源,並且因此完成清理工作。有一件你必須注意的事情是我們在清理資源之後立即調用了GC.SupressFinalize(this)。這個方法通知垃圾搜集器不需要調用解構器,因為我們已經做了清理。
注意上面的例子,解構器使用參數false調用Dispose。這裡,我們確信垃圾搜集器搜集了托管資源。我們僅僅做非托管資源的清理。
結論
盡管如此我們花費一些時間實現IDisposable接口,如果客戶不能合適地調用它們會怎樣?為此C#有一個酷的解決方案。‘using’代碼塊。它看起來像這樣:
using (MyClass objCls =new MyClass()){}
當控制從using塊通過成功運行到結束或者拋出異常退出時,MyClass的IDispose.Dispose()將會被執行。記住你例示的對象必須實現System.IDisposable接口。using語句定義了哪個對象將被清除的一個范圍。