2012-05-18 34 views
7

後終結不叫我的測試代碼:未處理的異常甚至CriticalFinalizerObject

public class A : CriticalFinalizerObject 
{ 
    ~A() 
    { 
     File.WriteAllText("c:\\1.txt", "1z1z1"); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     A a = new A(); 
     throw new Exception(); 
    } 
} 

開始我嘗試運行它,而不從CriticalFinalizerObject推導。該程序結束後,終結器未被調用。 這讓我感到驚訝,因爲我認爲它更確定,但沒關係。然後我讀了關於確保他們的終結器將被調用的CriticalFinalizerObject。我從它得到A. 猜猜看是什麼。它仍然沒有被執行。 我在做什麼/理解錯誤?

(我知道,請不要寫關於垃圾收集是不確定性顯而易見的東西。作爲節目結束了,我想到後一個漂亮的未處理管理例外,我可以放心地收拾這是不是這樣的)

回答

9

首先,讓我們瞭解CriticalFinalizerObjectMSDN,我們可以看到,認爲:

在來自評論家的類如果終結器遵循CER的規則,公共語言運行時(CLR)保證所有關鍵終止代碼都有機會執行,即使在強制CLR卸載應用程序域或終止一個應用程序域的情況下線。

這裏的主要詞是UNLOAD

其次,讓我們再次閱讀MSDN,這個時間大概例外管理線程:

如果這些例外是在主線程中未處理的,或在進入從非託管代碼運行的線程,他們正常進行,導致終止的申請。

主要字是終止

因此,當主線程中出現未處理的異常時 - 應用程序終止,但CriticalFinalizerObject僅幫助卸載域。

例如,CriticalFinalizerObject可以幫助在這樣的情況:

// Create an Application Domain: 
AppDomain newDomain = AppDomain.CreateDomain("NewApplicationDomain"); 

// Load and execute an assembly: 
newDomain.ExecuteAssembly(@"YouNetApp.exe"); 

//Unload of loaded domain 
AppDomain.Unload(newDomain); 

這是一種情況,即域名被卸下,並CriticalFinalizerObject向你保證,你的終結將被調用。

與應用程序的終止你的情況,你可以嘗試訂閱

AppDomain.CurrentDomain.UnhandledException 

和手動完成你的對象。

UPD: 傑弗裏裏希特在「通過C#CLR」一書中寫道CriticalFinalizerObject,這是爲您發送您的代碼,例如到SQLServer的情況下,它可以運行C#作爲一個程序。在這種情況下,如果SQLServer將卸載您的庫的域,CriticalFinalizerObject可幫助您清理對象。 另外CriticalFinalizerObject用於需要在對象的終結器中調用另一個對象的方法的情況,因爲CriticalFinalizerObject保證你,它的終結器將在所有非CriticalFinalizerObject對象的終結器之後被調用。

+0

感謝您的詳細解答! 我會說在這些MS文檔中沒有很好地定義「終止」與「未加載」等詞語。通常情況下,「終止」並不一定意味着它不會被「卸載」,雖然我們在這裏看到它不是這樣的...... – IlyaP

+0

我添加了一些更新來回答「CLR via C#」 – igofed

0

好的。寫了一個簡單的測試。 如果我在創建我的A對象的構造函數中有異常,實際上無法使終結器工作,但是當我創建的對象不在其他類的構造函數中但是在其他方法中時,然後拋出異常時它就起作用。因此,我懷疑,如果構造函數從未完成構造類,並且其創建已終止,則從未創建對象,堆棧已清除,對象將從堆中刪除,而不會像從未發生過。

這是我的猜測。但爲了解決這個問題,我會在try-catch-finally中關鍵地構造代碼構造對象,並明確地調用清理代碼。

舉例: 這個工作

public Form1() 
     { 
      InitializeComponent(); 
     } 
     protected override void OnLoad(EventArgs e) 
     { 
      base.OnLoad(e); 
      var a = new A(); 
      throw new Exception(); 
     } 
    } 

這並沒有

public Form1() 
     { 
      InitializeComponent(); 
      var a = new A(); 
      throw new Exception(); 
     } 

    } 
+0

它沒有被調用 - 正如你所看到的,我在終結器中創建了一個文件 - 它從不出現。 – IlyaP

+0

GC.Collect()在哪裏?如果在拋出異常之前,它肯定會起作用。但是我不能在拋出後寫任何代碼... – IlyaP

+1

要處理未處理,您仍然可以使用AppDomain.CurDomain.UnhandledException事件。 –