程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C#銳利體驗(五)(5)

C#銳利體驗(五)(5)

編輯:關於C語言

實際上C#編譯器不允許用戶自己重載或調用Finalize方法--編譯器徹底屏蔽了父類的Finalize方法(由於C#的單根繼承性質,System.Object類是所有類的祖先類,自然每個類都有Finalize方法),好像這樣的方法根本不存在似的。我們看下面的代碼實際上是錯的:

using System;
class MyClass
{
    override protected void Finalize() {}// 錯誤
    public void MyMethod()
    {
        this.Finalize();// 錯誤
    }
}

但下面的代碼卻是正確的:

using System;
class MyClass
{
    public void Finalize()
    {
        Console.WriteLine("My Class Destructor");
    }
}
public class Test
{
    public static void Main()
    {
        MyClass MyObject=new MyClass();
MyObject.Finalize();
    }
}

實際上這裡的Finalize方法已經徹底脫離了“終止化操作”的語義,而成為C#語言的一個一般方法了。值得注意的是這也屏蔽了父類System.Object的Finalize方法,所以要格外小心!

終止化操作在.Net運行時裡有很多限制,往往不被推薦實現。當對一個對象實現了終止器(Finalizer)後,運行時便會將這個對象的引用加入一個稱作終止化對象引用集的隊列,作為要求終止化的標志。當垃圾收集開始時,若一個對象不再被引用但它被加入了終止化對象引用集的隊列,那麼運行時並不立即對此對象進行垃圾收集工作,而是將此對象標志為要求終止化操作對象。待垃圾收集完成後,終止化線程便會被運行時喚醒執行終止化操作。顯然這之後要從終止化對象引用集的鏈表中將之刪去。而只有到下一次的垃圾收集時,這個對象才開始真正的垃圾收集,該對象的內存資源才被真正回收。容易看出來,終止化操作使垃圾收集進行了兩次,這會給系統帶來不小的額外開銷。終止化是通過啟用線程機制來實現的,這有一個線程安全的問題。.Net運行時不能保證終止化執行的順序,也就是說如果對象A有一個指向對象B的引用,兩個對象都有終止化操作,但對象A在終止化操作時並不一定有有效的對象A引用。.Net運行時不允許用戶在程序運行中直接調用Finalize()方法。如果用戶迫切需要這樣的操作,可以實現IDisposable接口來提供公共的Dispose()方法。需要說明的是提供了Dispose()方法後,依然需要提供Finalize方法的操作,即實現假托的析構函數。因為Dispose()方法並不能保證被調用。所以.Net運行時不推薦對對象進行終止化操作即提供析構函數,只是在有非受管資源如數據庫的連接,文件的打開等需要嚴格釋放時,才需要這樣做。

大多數時候,垃圾收集應該交由.Net運行時來控制,但有些時候,可能需要人為地控制一下垃圾回收操作。例如在操作了一次大規模的對象集合後,我們確信不再在這些對象上進行任何的操作了,那我們可以強制垃圾回收立即執行,這通過調用System.GC.Collect() 方法即可實現,但頻繁的收集會顯著地降低系統的性能。還有一種情況,已經將一個對象放到了終止化對象引用集的鏈上了,但如果我們在程序中某些地方已經做了終止化的操作,即明確調用了Dispose()方法,在那之後便可以通過調用System.GC.SupressFinalize()來將對象的引用從終止化對象引用集鏈上摘掉,以忽略終止化操作。終止化操作的系統負擔是很重的。

在深入了解了.Net運行時的自動垃圾收集功能後,我們便會領會C#中的析構器為什麼繞了這麼大的彎來實現我們的編程需求,才能把內存資源和非內存資源的回收做的游刃有余--這也正是析構的本原!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved