C# 中的解構函式實際上是重寫了 System.Object 中的虛方法
Finalize
三種最常的方法如下:
1. 解構函式;(由GC調用,不確定什麼時候會調用)
2. 繼承IDisposable介面,實現Dispose方法;(可以手動調用。比如資料庫的串連,SqlConnection.Dispose(),因為如果及時釋放會影響資料庫效能。這時候會用到這個,再如:檔案的開啟,如果不釋放會影響其它操作,如刪除操作。調用Dispose後這個對象就不能再用了,就等著被GC回收。)
3. 提供Close方法。(類似Dispose但是,當調用完Close方法後,可以通過Open重新開啟)
解構函式不能顯示調用,而對於後兩種方法來說,都需要進行顯示調用才能被執行。而Close與Dispose這兩種方法的區別在於,調用完了對象的Close方法後,此對象有可能被重新進行使用;而Dispose方法來說,此對象所佔有的資源需要被標記為無用了,也就是此對象要被銷毀,不能再被使用。例如常見.Net類庫中的SqlConnection這個類,當調用完Close方法後,可以通過Open重新開啟一個資料庫連接,當徹底不用這個對象了就可以調用Dispose方法來標記此對象無用,等待GC回收。明白了這兩種方法的意思後,大家在往自己的類中添加的介面時候,不要歪曲了這兩者意思。
解構函式
Dispose方法
Close方法
意義
銷毀對象
銷毀對象
關閉對象資源
調用方式
不能被顯示調用,在GC回收是被調用
需要顯示調用
或者通過using語句
需要顯示調用
調用時機
不確定
確定,在顯示調用或者離開using程式塊
確定,在顯示調用時
下面提供一個模式來結合上面的 解構函式和Dispose方法。
public class BaseResource: IDisposable
{
//前面我們說了解構函式實際上是重寫了 System.Object 中的虛方法 Finalize, 預設情況下,一個類是沒有解構函式的,也就是說,對象被記憶體回收時不會被調用Finalize方法
~BaseResource()
{
// 為了保持代碼的可讀性性和可維護性,千萬不要在這裡寫釋放非託管資源的代碼
// 必須以Dispose(false)方式調用,以false告訴Dispose(bool disposing)函數是從記憶體回收行程在調用Finalize時調用的
Dispose(false);
}
// 無法被客戶直接調用
// 如果 disposing 是 true, 那麼這個方法是被客戶直接調用的,那麼託管的,和非託管的資源都可以釋放
// 如果 disposing 是 false, 那麼函數是從記憶體回收行程在調用Finalize時調用的,此時不應當引用其他託管對象所以,只能釋放非託管資源
protected virtual void Dispose(bool disposing)
{
// 那麼這個方法是被客戶直接調用的,那麼託管的,和非託管的資源都可以釋放
if(disposing)
{
// 釋放 託管資源
OtherManagedObject.Dispose();
}
//釋放非託管資源
DoUnManagedObjectDispose();
// 那麼這個方法是被客戶直接調用的,告訴記憶體回收行程從Finalization隊列中清除自己,從而阻止記憶體回收行程調用Finalize方法.
if(disposing)
GC.SuppressFinalize(this);
}
//可以被客戶直接調用
public void Dispose()
{
//必須以Dispose(true)方式調用,以true告訴Dispose(bool disposing)函數是被客戶直接調用的
Dispose(true);
}
}
上面的範例達到的目的:
1/ 如果客戶沒有調用Dispose(),未能及時釋放託管和非託管資源,那麼在記憶體回收時,還有機會執行Finalize(),釋放非託管資源,但是造成了非託管資源的未及時釋放的空閑浪費
2/ 如果客戶調用了Dispose(),就能及時釋放了託管和非託管資源,那麼該對象被記憶體回收時,不回執行Finalize(),提高了非託管資源的使用效率並提升了系統效能
最後:
如果您的類中使用了非託管資源,則要考慮提供Close方法,和Open方法。並在您的Dispose方法中先調用 Close方法。
在使用已經 有類時,如SqlConnection。如果暫時不用這個串連,可以考慮用Close()方法。如果不用了就考慮調用
Dispose()方法。