2016-08-02 34 views
44

我已經看到了這個例子:爲什麼在C#6.0中交換內部'finally'和'outer'的執行順序?

static void Main(string[] args) 
{ 
    Console.WriteLine("Start"); 
    try 
    { 
     SomeOperation(); 
    } 
    catch (Exception) when (EvaluatesTo()) 
    { 
     Console.WriteLine("Catch"); 
    } 
    finally 
    { 
     Console.WriteLine("Outer Finally"); 
    } 
} 

private static bool EvaluatesTo() 
{ 
    Console.WriteLine($"EvaluatesTo: {Flag}"); 
    return true; 
} 

private static void SomeOperation() 
{ 
    try 
    { 
     Flag = true; 
     throw new Exception("Boom"); 
    } 
    finally 
    { 
     Flag = false; 
     Console.WriteLine("Inner Finally"); 
    } 
} 

將會產生下一個輸出:

Start 
EvaluatesTo: True 
Inner Finally 
Catch 
Outer Finally 

這聽起來怪我,我期待這個順序來包裝它的一個很好的解釋在我的腦海中。我期待when前要執行的finally塊:

Start 
Inner Finally 
EvaluatesTo: True 
Catch 
Outer Finally 

的文檔指出這個執行順序是正確的,但它並沒有在它爲什麼這樣做了闡述和究竟是執行規則在此點餐。

+0

@RahulTripathi不,我的問題是關於在C#6.0中交換執行順序,這個問題在6.0之前有一個在香草C#中的直接執行順序 – Archeg

+3

這是一個不可避免的結果,當*子句起作用時。 CLR無法確定最終需要執行的內容,直到*找出catch子句將處理異常之後。只有這樣它才能開始展開堆棧,最終執行塊。 when表達式的可能的無意的副作用是爲什麼在v6之前它被排除在C#語言之外。 –

+0

@HansPassant具體而言,這是如此:「CLR無法弄清楚最終需要執行的內容,直到它找出catch子句將處理異常爲止。」? –

回答

36

您可能已經被教導過,當發生異常處理時,每種方法都會被分開考慮。也就是說,由於你的內部方法有一個try...finally,任何異常將首先觸發finally,然後它將「查找」更高的try。這是不正確的。

從CLR的ECMA規範(ECMA-335,異常處理I.12.4.2.5概述):

當異常發生時,CLI搜索所述第一受保護的塊陣列

  • 保護包括當前指令指針
  • 的區域是catch處理方框
  • 其過濾希望處理異常

如果在當前的方法沒有找到匹配,調用的方法搜索,等等。如果找不到匹配,CLI將轉儲堆棧跟蹤並中止程序。

如果找到匹配項,CLI將堆棧移回剛纔找到的位置,但這次調用finally和fault處理程序。然後它啓動相應的異常處理程序。

正如您所看到的,該行爲與規範100%兼容。

  1. 尋找一個保護塊 - trySomeOperation
  2. 它有一個catch處理程序塊?號
  3. 尋找在調用方法保護塊 - tryMain
  4. 它有一個catch處理程序塊?是!
  5. 過濾器是否希望處理異常?評估過濾器(免責聲明:這並不意味着保護區塊中的所有過濾器將始終被評估 - 如果過濾器沒有任何副作用,它當然不應該出現問題),以及結果是肯定的。
  6. 走棧背部和執行所有最終和故障處理
    1. finallySomeOperation

Mainfinally是不是其中的一部分,當然 - 它會執行時,執行不管什麼異常,都會離開受保護的塊。

編輯:

只是爲了完整性 - 這一直是這樣。唯一改變的是C#現在支持異常過濾器,它允許您觀察執行順序。 VB.NET支持第1版異常過濾器。

+0

哇。事實證明,我不知道如何最終在C#中實際工作。看起來似乎有很多人做過。你知道爲什麼C#團隊決定像這樣實現它的任何解釋,而不是你如何描述它在第一段? – Archeg

+0

@Archeg - 我還在努力的一件事是你的論點,這在C#6.0中發生了某種變化......你是說這是*而不是*它之前的表現如何? –

+0

@Archeg嗯,我想你從來沒有在Windows上使用過結構化的異常處理:)其要點是,這種行爲在Windows上可以免費使用,並且異常過濾器不應該引起副作用,那麼爲什麼你會做另一種方式? – Luaan

-5

A finally塊總是執行,不管是否引發異常。

finally塊來執行兩種:

  • 後catch塊完成
  • 控制離開,因爲跳轉語句的try塊後(例如,返回或轉到)
  • try塊結束後

可以擊敗finally塊的唯一的東西是一個無限循環或進程突然發送。 finally塊有助於向程序添加確定性

+2

我不明白這是如何回答這個問題的。你提供了關於'finally'塊的一般性解釋,當問題是非常具體的,並且實際上是關於'when'時。 – svick

相關問題