2010-09-02 52 views
6

我總是努力在哪裏放置一個try catch塊。例如,我有一個接受兩個參數的方法的數據庫類。 FindObject(string where,string order)。此方法使用指定的where和order字符串執行sql查詢。在哪裏實現try catch塊?

在一個類中我有一個名爲IsUsed屬性,這個屬性是這樣的:

public bool IsUsed 
{ 
get 
{ 
    ClassA a = new ClassA(); 
    Collection<ClassA> myCollection = a.FindObject("Id = 1",""); 

    if(..) // etc 
} 
} 

,如果這種做法是聰明與否沒關係,我只是想知道在何處放置試如果sql查詢的執行出錯,就趕上。

我應該在哪裏放置try catch,以便我可以通知用戶出了什麼問題?

  1. 在FindObject方法中?
  2. 在IsUsed屬性中?
  3. 我在哪裏調用IsUsed屬性?
  4. 其他地方?但是,在
+0

可能的重複,雖然不是特定於C#:http://stackoverflow.com/questions/2119780/try-catch-blocks-inside-or-outside-of-functions-and-error-handing – 2010-09-02 08:16:55

回答

6

只有在你能做些什麼的情況下才能發現異常。否則,在您的應用程序中以「最高」級別捕獲異常,並執行所需的任何操作來處理它,包括終止您的應用程序。

在具有UI的應用程序中,「最高」級別通常是事件處理程序,例如搜索按鈕的點擊處理程序。對於後臺服務來說,「最高」級別通常是線程特效或定時器回調。

UI application       Background service 

| System Code |      | System Code | 
+----------------+      +----------------+ 
| Event Handler | <- try/catch here -> | Thread proc | 
|    |      |    | 
| ...   |      | ...   | 
|    |      |    | 
| Method   | <- throw here  -> | Method   | 

如果你讓異常傳播回系統代碼,你將有一個未處理的異常,你的應用程序將崩潰。

在UI應用程序中處理異常通常涉及顯示一個消息框。一些異常並不致命,並且操作可能會重試(例如,如果文件丟失或數據庫查詢失敗),但其他異常是致命的,唯一的選擇是終止應用程序。

後臺服務將記錄異常並可能重試該操作。如果多次重試失敗,日誌記錄級別可能會增加,以引起操作員的注意。

當談到異常處理的良好做法:

  • 如果你發現一個異常並重新拋出您自己的異常封裝了原始異常作爲新的異常的InnerException
  • 如果您發現異常,可能需要做一些清理工作,但重新拋出異常,因爲您希望它冒泡,然後始終使用throw重新拋出它,而不指定任何異常。這將確保原始堆棧跟蹤不被破壞。
  • 在大多數情況下,您只能在頂級處理程序中捕獲基類Exception
  • 使用finally塊或甚至更好的IDisposable模式來執行代碼中的適當清理。
  • 將異常看作是開發人員的「用戶界面」,並相應地格式化異常消息。如果你需要更技術性更好的用戶界面,你應該隱藏更多技術性的東西。
  • 嘗試僅在異常情況下使用異常,例如意外錯誤。不要爲常見錯誤情況拋出異常。檢查字典中鍵的存在不應引發異常,而是返回true/false值。

要回答你的具體問題,我不認爲你應該捕捉你的財產的任何例外。

11

我儘量遵循一個相當簡單的規則:我設置了一個try..catch塊如果我能以一種合理的方式處理異常。如果我對異常沒有任何意義,我會讓它冒泡到調用代碼中。

作爲一個旁註,我會避免在屬性獲取器中執行(可能冗長的)數據庫調用。我通常試圖讓屬性剛剛設置或從後臺字段獲取值,讓其他方法執行數據庫查找等。這會使代碼對於編寫調用代碼的人更具可預測性(對於屬性訪問來說這是一個便宜的操作,而方法調用通常可能更昂貴)。

+0

謝謝。我會用你的方法。我應該在IsUsed方法中引發一個消息框來通知用戶發生了異常嗎? – Martijn 2010-09-02 08:24:02

+1

@Martijn:在不瞭解代碼的情況下,我不能說是否顯示消息框是個好主意,但我儘量避免這樣做,除非代碼非常接近最終用戶。如果代碼位於類庫中,則顯示消息框有效使其無法在Web應用程序或類似應用程序中使用。 – 2010-09-02 08:42:37

+1

我還想補充一點,這可能是一種很好的方法來讓異常被拋出,但有時候你也必須使用'finally'塊(特別是對於DB調用,確保關閉連接 - 如果不使用'使用「塊」)。在這種情況下,您只需在'catch'部分重新拋出異常。 – 2010-09-02 08:44:24

3

你應該把try-cacth塊放在你可以做一些有意義的事情的地方。就像你登錄或顯示給用戶一樣。不要僅僅因爲它而捕捉異常。

+0

但我的觀點是,向用戶展示它的最佳位置是什麼? – Martijn 2010-09-02 08:24:37

2

那麼它應該在哪裏try..catch塊是需要的,並且它可以被處理。我假設這將在FindObject方法。

因此,在FindObject方法中,捕獲並處理例如SqlException。如果需要將其移至更高級別以獲得更好的處理,請拋出異常,或讓它簡單地冒泡。

+0

如果該方法失敗,我想通知用戶發生了數據庫錯誤。我該如何以及在哪裏做這件事? – Martijn 2010-09-02 08:25:45

+0

@Martijn - 最好的做法是將數據庫代碼從'IsUsed'屬性移開,並且有一個方法調用它。然後,您可以從某種初始化代碼的方法中處理異常。對屬性的數據庫調用可能會變得很難。而是使用初始化代碼並設置屬性值。 – 2010-09-02 08:44:07

2

我能想到的唯一規則就像是「在最低級別上,您可以實際上對信息做一些有用的事情,而且不會重複執行異常處理代碼」。例如,如果您通常希望以相同的方式捕獲所有與數據庫訪問相關的異常,我會創建一個數據抽象層(有很多很好的理由可以這麼做),並將try-catch塊放在那裏。

另一個極端將是一個Web應用程序,您不希望出現任何此類異常,並且Elmah會捕獲異常。在這種情況下,你根本不想使用try-catch塊,因爲它會搞砸日誌。

2

它很容易回答:你可以在哪裏適當地處理它。

I.e,在特定的地方,你能否根據這個問題做出有用的決定?你能回到別的東西嗎?你能告訴某人什麼嗎?你能否將錯誤轉化爲對應用程序的其他層更有用的錯誤?

如果答案是肯定的,那麼你可以在那裏找到答案。如果不是所有這些,那麼不要抓住它,讓另一個區域這樣做。

FWIW,恕我直言,你的Get執行也過於複雜。我認爲通常情況下,人們不會期望一個財產做這種「工作」。但我的意見。

0

就我而言,我把嘗試捕捉塊:

  • 最後需要清理ressources
  • 時,需要在失敗的情況下適用的操作流程(用戶重試,日誌記錄...)

通常,我讓異常冒泡到頂層,通常是一些winform控件事件處理程序。我用在這裏試着抓住,使用應用程序級別的異常處理方法。

否則,當我特別懶惰,我鉤Application.ThreadException事件到MessageBox.Show靜態無效的主要()入口點方法。

+2

但是你不需要一個catch塊來有一個finally塊。 – Polyfun 2010-09-02 10:06:10

-1

請參閱,如果您遇到任何錯誤將IsUsed設置爲默認值。這樣的事情:

public bool IsUsed 
{ 
get 
{ 
    try{ 
    ClassA a = new ClassA(); 
    Collection<ClassA> myCollection = a.FindObject("Id = 1",""); 

    if(..) // etc 
    } 
    catch { return false;} 
} 
} 

所以,每當在FindObject中有任何錯誤,您將產生false。

+0

我想過這種方法,但我不喜歡,因爲這樣我永遠不知道是否發生了數據庫異常。所以這使得調試更加困難.. – Martijn 2010-09-02 08:33:58

+0

@Martijn:是的。永遠不要吞噬異常,除非它真的是虛假的,或者是不需要成功的操作的一部分,例如調試日誌記錄(對於某些程序)。 – siride 2010-09-02 14:33:17

+0

@Martijn當然如果你需要的話,你可以拋出異常。但是通常情況下,如果屬性發生異常,我們只返回屬性的默認值。如果您的例外情況非常敏感,那麼您可以記錄例外消息並向用戶顯示一條好消息,而不是停止整個程序。 – abhishek 2010-09-04 15:22:28

1

一般來說,作爲rule of thumb

的try塊包含保護代碼是可能導致異常

內部在你的情況,你可以處理它想:

FindObject() 
{ 
try{}catch{throw;//or throw new Exception("Some info"); 
} 

IsUser 
{ 
try{...a.FindObject("Id = 1",""); return true;} 
catch(Exception ex){Log(ex); return false;} 
} 

- EDIT--

這是響應@controlbreak評論:

MSDN說:

您可以顯式地拋出使用throw語句異常。您還可以使用throw語句再次引發捕獲的異常。 好的編碼練習將信息添加到重新拋出的異常,以便在調試時提供更多信息。

+0

爲什麼要捕捉一個異常,什麼也不做,然後再扔掉? – Martijn 2010-09-02 08:34:52

+0

catch {throw;}是unusefull。無論如何,很高興提及catch(單獨(不是catch(Exception ...))可以用來捕獲非託管異常 – Larry 2010-09-02 08:41:44

+0

@Martinjn:你捕獲異常並再次拋出,因爲你目前沒有處理程序它不知道你想要怎麼處理這個特定的錯誤,但是你通過重新拋出它來讓它「起泡」,這樣某個「上層」可能知道該怎麼處理這個錯誤。捕捉一個「服務器忙」的異常,你可以通過嘗試再次連接來處理它,但你也會發現一個異常說「服務器關閉」,現在你不知道該怎麼做......所以你把它泡了起來。調用方法可以很好地捕捉和處理錯誤 – ace 2010-09-02 08:43:48

1

這取決於您想要處理的地方繼續處理。假設調用代碼負責通知用戶,那麼一個好的地方就是調用代碼。在通知用戶之後,調用代碼可能需要向用戶提問。

還考慮重新構造引發的異常。如果IsUsed駐留在與數據庫無關的類中並且FindObject可以拋出SqlServerTimedOutException,它將彈出整個調用堆棧。如果這schemantics是完全混亂捕獲異常並重新拋出這樣的:

 public bool IsUsed 
    { 
     get 
     {  
      ClassA a = new ClassA();  
      Collection<ClassA> myCollection; 

      try 
      { 
       myCollection = a.FindObject("Id = 1",""); 
      } 
      catch (SqlServerTimedOutException ex) 
      { 
       throw new MyApplicationException("Cannot evaluate if is in use.", ex); 
      } 

      if(..) // etc 
     } 
    } 

但不要濫用這一點,因爲它使代碼比較難看。慎重考慮。

-1

我不同意你們大多數人。

首先,我想喬爾推薦這個偉大的帖子:http://www.joelonsoftware.com/items/2003/10/13.html

牢記這一點,我使用try-catch塊僅接近他們可以去錯了地。
顯然,用戶界面想要了解它。讓它具有一個錯誤值,但不要隱藏實際問題的原因(通過捕獲其他意外錯誤)。

如果我正確理解你的代碼,我想我會在實際查詢中使用try-catch,並讓IsUsed nullabe確保我明白它沒有賦值。

+0

理論上聽起來不錯,但事實是,在拋出它的地方,你可能無法處理異常。我的意思是,也許你可以,並在這種情況下,通過一切手段抓住和處理。但要求每個級別「處理」每一個錯誤不僅會造成難看的代碼,而且可能會產生不正確的代碼。原因:生成異常的代碼實際上可能不足以處理它,只有較高層纔會有這些信息(例如,決定是否重試)。 – siride 2010-09-02 14:31:29

0

儘早捕獲錯誤,並設計API調用,以便在不使用異常的情況下處理失敗。未捕獲的異常是穩定性的威脅,並且您可能不知道是否存在邊緣情況中出現的異常,除非較低的API表示它們可能會失敗。

通常你可以設計api來讓結果對象保持執行狀態,這會告訴消費開發者這種方法可能會失敗以及失敗。

這樣的方法通常會給你有意義的方式來處理低級API部分中的異常,而不是讓它們向上調用代碼。

這就是說,有些語言可以聲明一個方法會拋出異常,拋出哪些異常,但是你編寫的C#沒有這個功能。