2009-12-30 72 views
11

什麼時候適合使用ThrowHelper而不是直接投擲?什麼時候應該使用ThrowHelper方法而不是直接投擲?

void MyMethod() { 
    ... 
    //throw new ArgumentNullException("paramName"); 
    ThrowArgumentNullException("paramName"); 
    ... 
} 
void ThrowArgumentNullException(string paramName) { 
    throw new ArgumentNullException(paramName); 
} 

我讀過,調用ThrowHelper方法(與拋出異常的唯一方法purpouse),而不是直接扔應該產生較小的字節碼。

這個和明顯的封裝(另一個間接層)可能是不直接拋出的好理由,至少在某些場景中是這樣。

無論如何,國際海事組織的弊端也不是非實質性的。

  • 的(例外)控制流程被隱藏
  • 異常的一部分最終具有更隱蔽棧跟蹤
  • 編譯器(2.0)將無法識別ThrowHelper調用是出口點從一種方法,因此一些代碼是必要的。

我有限的經驗是,總體設計往往變得更糟。

int MyMethod(int i) { 
    switch (i) { 
     case 1: 
      return 1; 
     default: 
      ThrowMyException(); 
    } 
    return 0; // Unreachable (but needed) code 
} 

這可能部分是個人品味的問題。無論如何,你對這個問題的個人指導方針是什麼?你是否發現使用ThrowHelpers來執行所有常見任務,如方法參數驗證(ThrowArgumentNullException(paramName)等)是一個好主意? 我在這個問題上錯過了一些明顯的東西嗎?

btw我試圖不要混淆這個問題與驗證問題,例如,一種方法,如:

ThrowIfNameIsNullOrEmpty(name); 
+3

哇。我以前沒有看到過這種模式,但是和我一樣,我的直接反應非常消極。對絕大多數情況來說,字節碼大小必定是不成熟的優化。 – bobince 2009-12-30 13:20:24

+1

微軟在設計Stack時使用ThrowHelper :http://pastebin.com/p2k4URtU – 2012-05-18 19:54:11

回答

13

我的默認方法是直接從特殊代碼分支中拋出。

DRY原則和3個指南的規則,當我將它包裝在一個方法中時:如果我發現自己編寫相同的'throw'代碼3次或更多次,我考慮將其包裝在輔助方法中。

然而,而不是拋出的方法,這是更好的編寫創建所需的異常工廠方法,然後從原來的地方把它:

public void DoStuff(string stuff) 
{ 
    // Do something 

    throw this.CreateException("Boo hiss!"); 
} 

private MyException CreateException(string message) 
{ 
    return new MyException(message); 
} 

這會保留堆棧跟蹤。

1

我總是試圖拋出我自己的例外,僅僅是爲了定製的目的。我可以發出一個消息,告訴我在短期內問題是什麼(可能來自哪裏)。就性能問題而言,我儘可能地儘量限制在我的軟件發行版本上拋出的異常數量,所以我從來沒有真正想過太多。我有興趣看看其他人說什麼。

這是我的2美分。請這樣拿。

7

這對我來說似乎是一個不必要的抽象。你有一種方法可以完成一件事,就像它所做的事情一樣,被稱爲冗長的。

有關較少字節碼的爭論實際上毫無意義,因爲代碼的大小很少重要(除非您從您的程序的一個荒謬數目的地方拋出異常,否則不會節省超過每個程序實例的千字節源代碼)。同時你所說的缺點都是真正的問題,特別是在涉及異常處理的清晰度方面。

基本上抽象這樣的小事通常會回來咬你。 (一些有趣的閱讀主題:http://www.joelonsoftware.com/articles/LeakyAbstractions.html

1

從我所瞭解的,較小的字節碼幾乎是唯一的優勢(See this question),所以我不指望你會看到很多參數在這裏的答案。

4

我想說這樣做的唯一合理時間就在BCL這樣的地方,那裏的課程使用量足夠廣泛,節省可能是值得的。然而,這將是有道理的。就你而言,我認爲你實際上並沒有節省任何空間。 BCL中ThrowHelper的實例通過將枚舉替換爲字符串和方法調用(以獲取字符串消息)來減小大小。你的情況只是通過相同的論點,因此沒有實現任何節約。調用您的方法的成本與拋出異常的成本一樣多。

+0

明白你的觀點。如果權重不是使用'throw'關鍵字本身,而是參數傳遞,這種幫助器不會提高性能。 – smv 2009-12-30 13:43:53

0

使用throw-helper或exception-factory方法還沒有提到的一個優點是可以爲此目的傳遞一個委託。當使用投擲輔助代表時,可以獲得不投擲的可能性(在某些情況下,這可能是一件好事;在其他情況下,這是一件壞事)。例如:

 
string TryGetEntry(string key, Funct<problemCause, string> errorHandler) 
{ 
    if (disposed) 
    return errorHandler(problemCause.ObjectDisposed); 
    else if (...entry_doesnt_exist...) 
    return errorHandler(problemCause.ObjectNotFound); 
    else 
    return ...entry...; 
} 
string GetEntry(string key) 
{ 
    return TryGetEntry(key, ThrowExceptionOnError); 
} 
bool TryGetEntry(string key, ref result) 
{ 
    bool ok; 
    string result; 
    result = TryGetEntry(key, (problemCause theProblem) => {ok=false; return (string)null;}); 
    return ok; 
} 

使用這種方法,可以很容易地使用一種具有各種錯誤處理策略的例程。通過讓TryGetEntry方法接受一個泛型類型參數以及該類型的'ref'參數和接受這種'ref'參數的委託,可以消除對閉包的需求。這個例子很簡單,只是使用閉包。

+0

這很聰明,但我更喜歡更簡單的方法......只是不要擔心錯誤處理程序中的錯誤... :-) – smv 2012-04-24 21:02:37

相關問題