2008-08-09 179 views
7

性能考慮我所遇到以下類型的代碼,很多時候,我不知道這是一個很好的做法(從性能的角度來看)或不:投擲例外

try 
{ 
    ... // some code 
} 
catch (Exception ex) 
{ 
    ... // Do something 
    throw new CustomException(ex); 
} 

基本上,編碼器是什麼正在做的是,他們正在包含自定義異常中的異常並再次拋出異常。

這是如何在性能上不同於以下兩種:

try 
{ 
    ... // some code 
} 
catch (Exception ex) 
{ 
    .. // Do something 
    throw ex; 
} 

try 
{ 
    ... // some code 
} 
catch (Exception ex) 
{ 
    .. // Do something 
    throw; 
} 

拋開任何功能或編碼最佳實踐的論點,有3種方法之間的性能差異?

回答

10

@Brad Tutterow

異常不被在第一種情況下丟失的,它被在傳遞給構造函數。儘管如此,我會同意你的意見,但第二種方法由於堆棧跟蹤丟失而變得非常糟糕。當我和.NET一起工作時,我遇到了很多其他程序員都這樣做的情況,當我需要查看異常的真正原因時,它讓我感到沮喪,只是發現它從一個巨大的try塊中重新排列我現在不知道問題出在哪裏。

我也是第二個布拉德的評論,你不應該擔心的表現。這種微觀優化是一個可怕的想法。除非您正在討論在運行很長時間的for循環的每次迭代中拋出異常,否則您將很有可能不會因爲異常使用而遇到性能問題。

當您有指示您需要優化性能的指標時,請始終優化性能,然後點擊已證明是罪魁禍首的點。

通過簡單的調試功能(IE不隱藏堆棧跟蹤)而不是讓事情運行速度提高一個納秒更好。

關於將異常封裝到自定義異常中的最後一個註記...這可能是一個非常有用的構造,特別是在處理UI時。您可以將每個已知和合理的例外情況包裝到某個基本自定義異常(或從所述基本異常擴展而來的異常情況)中,然後UI可以捕獲此基本異常。當被捕獲時,該異常將需要提供向用戶顯示信息的手段,例如ReadableMessage屬性或沿着這些行的某些內容。因此,任何時候UI錯過了一個異常,這是因爲你需要修復一個錯誤,並且只要它捕獲到一個異常,它就是一個已知的錯誤條件,可以並且應該由UI正確處理。

0

您的第一個示例中的throw具有創建新的CustomException對象的開銷。

第二個例子中的重新拋出將拋出異常類型的異常。

第三個示例中的重新拋出將拋出與您的「某些代碼」拋出的相同類型的異常。

所以第二個和第三個例子使用較少的資源。

2

像大衛一樣,我想第二和第三位表現更好。但是,三個人中的任何一個表現不佳,不會花時間擔心嗎?我認爲有比性能更大的問題需要擔心。

FxCop始終建議採用第三種方法,以便原始堆棧跟蹤不會丟失。

編輯:刪除了那些明顯錯誤的東西,而且Mike很友善地指出。

2

很明顯,您會因爲創建新對象(新的Exception)而受到懲罰,就像您對程序附加的每一行代碼一樣,您必須決定更好的異常分類是否支付額外的工作。

作爲做出該決定的一條建議,如果您的新對象沒有攜帶有關異常的額外信息,那麼您可以忘記構造新的異常。

但是,在其他情況下,對於您的類的用戶來說,擁有一個異常層次結構是非常方便的。假設你實現Facade模式既不是目前考慮的方案是好的:

  1. 不好,你提高每一個例外的例外對象,因爲你失去(可能)的有價值的信息
  2. 是不好也不能提高你捕捉的每種對象,因爲你這樣做是因爲你在創建外觀時失敗

在這個假設的情況下,更好的做法是創建一個異常類層次結構,抽象你的用戶從系統的內在複雜性中,讓他們知道某種關於e的東西產生了xception。

補充說明:

我個人不喜歡使用異常的(從Exception類派生的類層次結構)來實現邏輯。類似的情況:

try { 
     // something that will raise an exception almost half the time 
} catch(InsufficientFunds e) { 
     // Inform the customer is broke 
} catch(UnknownAccount e) { 
     // Ask for a new account number 
} 
0

從純粹的性能角度來看,我猜想第三種情況是最高性能的。另外兩個需要提取堆棧跟蹤並構建新對象,這兩個對象都可能相當耗時。

話雖如此,這三個代碼塊有非常不同的(外部)行爲,因此比較它們就像詢問QuickSort是否比向紅黑樹添加項目更有效。這與選擇正確的事情並不重要。

1

正如其他人所說,最好的性能來自底部,因爲你只是重新拋出一個現有的物體。中間一個是最不正確的,因爲它失去了堆棧。

我個人使用自定義異常,如果我想解耦代碼中的某些依賴關係。例如,我有一個從XML文件加載數據的方法。這可能以許多不同的方式出錯。

它可能無法從磁盤讀取(FileIOException),用戶可能嘗試從不允許的地方訪問它(SecurityException),文件可能損壞(XmlParseException),數據可能格式錯誤(DeserialisationException)。

在這種情況下,調用類更容易理解所有這些,所有這些異常重新引發單個自定義異常(FileOperationException),這意味着調用者不需要對System.IO或System.Xml的引用,但仍然可以訪問通過枚舉和任何重要信息發生的錯誤。

如上所述,不要嘗試微觀優化這樣的事情,拋出異常的行爲是最慢的事情發生在這裏。要做的最好的改進就是儘量避免發生異常。

public bool Load(string filepath) 
{ 
    if (File.Exists(filepath)) //Avoid throwing by checking state 
    { 
    //Wrap anyways in case something changes between check and operation 
    try { .... } 
    catch (IOException ioFault) { .... } 
    catch (OtherException otherFault) { .... } 
    return true; //Inform caller of success 
    } 
    else { return false; } //Inform caller of failure due to state 
} 
2

不要做:

try 
{ 
    // some code 
} 
catch (Exception ex) { throw ex; } 

由於這將失去堆棧跟蹤。

而是做:

try 
{ 
    // some code 
} 
catch (Exception ex) { throw; } 

就扔會做,你只需要,如果你希望它是一個新的自定義異常的內部異常傳遞異常變量。

+1

小額外 - 刪除ex,否則您創建未使用的變量警告。 嘗試 { //一些代碼 } 趕上(例外) { 擲; } – Dennis 2009-04-24 04:50:06

0

Wait ....爲什麼我們關心性能,如果拋出異常?除非我們使用異常作爲正常應用程序流程的一部分(這是針對最佳實踐的WAYYYY)。

我只看到了關於成功的性能要求,但從來沒有關於失敗。