.Net中,垃圾回收器負責回收你創建的引用類型的對象,但是回收時間並不能准確估計出來,所以這稱之為非確定銷毀。值類型自動釋放,所以不在本文討論之中。
但是有些稀缺資源,比如文件句柄、數據庫連接等,就需要盡快釋放。如何做到呢。最簡單的方法就是調用GC.Collect ()強迫垃圾回收器工作。但是這種方法會降低性能,除非迫不得已。
那麼有沒有更好的辦法?
“析構函數”
“析構函數”與c++析構函數的區別
“析構函數”怎麼樣?比如下面的例子:
class Class1
{
public Class1()
{
Console.WriteLine("constructor");
}
~Class1()
{
Console.WriteLine("destructor");
}
}
其實c#中的“析構函數”並不是c++程序員心中的析構函數,在c++中,跳出對象所生存的堆棧時或者調用delete的時候,析構函數就會被調用。c#提供了一種語法相近,但是語義完全不同的“析構函數”.實際上,該函數只是重載了System.Object類的Finalize函數。
System.Object是c#中所有的類型的基類型,由於是隱式派生,所以不需要在派生列表中指定它。Finalize函數的訪問權限是protected,只能被自己和派生類使用,實際上在垃圾回收器銷毀對象時,將會給該對象一次調用Finalize的機會,這樣我們可以將自己需要的邏輯放在“析構函數”裡面。如果對象構造期間出現異常,則該方法也會被調用。
但是,經過以上分析,可以看出,“析構函數”並不能讓我們確定銷毀某對象。
“析構函數”的危害
順便說一下,C#代碼應該總是使用“析構函數”,而不是手動重載Finalize函數,因為“析構函數”會自動將內部代碼放到try塊內,並且在finnally塊內部調用base.Finalize方法。
“析構函數”正常情況下是不建議創建的,因為它會提升該類以及被引用的其他類的代齡,這會導致垃圾回收器過早的運行,從而影響性能,同時每次垃圾回收器工作時都要額外的調用“析構函數”,這顯然也是對性能有影響的,如無必要,就不要為自己的類創建“析構函數”。
更嚴重的情況,如果A類有“析構函數”,B類有“析構函數”,A有一個B類的變量,當垃圾回收器工作時,A和B的“析構函數”調用順序卻沒有得到保證。這樣會導致錯誤,這比性能問題更嚴重。
Oh,my!盡量不要在“析構函數”裡面訪問托管成員變量,除非銷毀非托管資源。但是通常非托管資源都需要確定銷毀,顯然這時不能用“析構函數”解決問題。
GC.SuppressFinalize ( )將阻止Finalize被調用。
IDisposable接口
不如提供一個方法,該方法能夠將需要提前釋放的資源釋放掉,然後該對象可以到垃圾回收器工作的時候再釋放。這就是Dispose方法。
class Class1:IDisposable
{
public Class1()
{
GC.SuppressFinalize(this);
Console.WriteLine("constructor");
}
~Class1()
{
Console.WriteLine("destructor");
}
public void Dispose()
{
GC.SuppressFinalize(this);
Console.WriteLine("Dispose");
}
}
GC.SuppressFinalize(this);這句話告訴垃圾管理器,從現在開始,不准調用我的“析構函數,當然不能調,因為我的Dispose後面還要執行清理資源的代碼,垃圾回收器如果搶在前面將對象回收,就會出錯。如果類沒有實現“析構函數”,當然就不需要這句話了。
Close方法
為了適應大多數程序員的習慣,可以添加一個Close方法,其實內部只是調用了Dispose而已。不高興的話,也可以懶得實現它。
Using 方法
為了減少編寫異常處理代碼,using語法可以保證無論是否發生異常,都為調用Dispose方法或者Close方法,前提是對象支持Idisposable接口。所以這是一個很簡潔的語法。