c# - catch效能 - try catch用途




為什麼在C#中捕獲並重新拋出異常? (10)

這不完全等同於不處理異常嗎?

不完全一樣,它是不一樣的。 它重置異常的堆棧跟踪。 雖然我同意這可能是一個錯誤,因此也是一個錯誤代碼的例子。

我正在查看可串行化DTO上的文章C# - 數據傳輸對象

文章包含這段代碼:

public static string SerializeDTO(DTO dto) {
    try {
        XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
        StringWriter sWriter = new StringWriter();
        xmlSer.Serialize(sWriter, dto);
        return sWriter.ToString();
    }
    catch(Exception ex) {
        throw ex;
    }
}

本文的其餘部分看起來理智和合理(對於noob),但是try-catch-throw會拋出WtfException ...... 這不完全等同於不處理異常嗎?

人機工程學:

public static string SerializeDTO(DTO dto) {
    XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
    StringWriter sWriter = new StringWriter();
    xmlSer.Serialize(sWriter, dto);
    return sWriter.ToString();
}

或者我錯過了一些關於C#中錯誤處理的基礎知識? 它幾乎與Java相同(減去檢查的異常),不是嗎? ......也就是說,他們都精煉了C ++。

堆棧溢出問題重新拋出無參數捕獲和不做任何事情之間的區別? 似乎支持我的觀點,即嘗試拋出是無效的。

編輯:

只是為了總結任何未來發現此線程的人...

不要

try {
    // Do stuff that might throw an exception
}
catch (Exception e) {
    throw e; // This destroys the strack trace information!
}

堆棧跟踪信息對於確定問題的根本原因至關重要!

try {
    // Do stuff that might throw an exception
}
catch (SqlException e) {
    // Log it
    if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
        // Do special cleanup, like maybe closing the "dirty" database connection.
        throw; // This preserves the stack trace
    }
}
catch (IOException e) {
    // Log it
    throw;
}
catch (Exception e) {
    // Log it
    throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
    // Normal clean goes here (like closing open files).
}

在較不具體的例外之前捕捉更具體的例外(就像Java一樣)。

參考文獻:


C#(在C#6之前)不支持CIL“過濾的異常”,這是VB所做的,因此在C#1-5中,重新拋出異常的一個原因是您在catch()時沒有足夠的信息。以確定您是否想要實際發現異常。

例如,在VB中你可以做

Try
 ..
Catch Ex As MyException When Ex.ErrorCode = 123
 .. 
End Try

......它不會用不同的ErrorCode值處理MyExceptions。 在v6之前的C#中,如果ErrorCode不是123,則必須捕獲並重新拋出MyException:

try 
{
   ...
}
catch(MyException ex)
{
    if (ex.ErrorCode != 123) throw;
    ...
}

由於C#6.0可以像使用VB一樣進行過濾

try 
{
  // Do stuff
} 
catch (Exception e) when (e.ErrorCode == 123456) // filter
{
  // Handle, other exceptions will be left alone and bubble up
}

人們沒有提到的一點是,儘管.NET語言並沒有真正做出區分,但是在發生異常時是否應該採取措施以及是否會解決它的問題實際上是不同的問題。 有很多情況下,人們應該根據例外情況採取行動,而這些例外是無望解決的,並且在某些情況下,“解決”例外所需的一切就是將堆棧解放到某一點 - 無需採取進一步的行動。

由於人們普遍認為人們只能“捕捉”可以“處理”的事物,所以在發生異常時應該採取行動的很多代碼不會。 例如,很多代碼會獲得一個鎖,將被保護的對象“暫時”置於一個違反其不變量的狀態,然後將其置於合法狀態,然後在其他人看到該對象之前釋放該鎖。 如果在對象處於危險無效狀態時發生異常,則通常的做法是釋放仍處於該狀態的對象的鎖定。 更好的方式是在對象處於“危險”狀態時發生異常,以明確地使鎖無效,以便將來嘗試獲取它時將立即失敗。 一致地使用這種模式將極大地提高所謂的“口袋妖怪”異常處理的安全性,因為代碼允許異常在沒有採取適當措施的情況下滲透,所以恕我直言得到了不好的聲譽。

在大多數.NET語言中,代碼根據異常採取行動的唯一方法是catch它(即使它知道它不會解決異常),執行相關操作,然後重新throw )。 如果代碼不關心拋出什麼異常,另一種可能的方法是在try/finally塊中使用ok標誌; 在塊之前將ok標誌設置為false ,並在塊退出之前以及在塊內的任何return之前將其設置為true 。 然後,在finally ,假設如果ok沒有設置,則必鬚髮生異常。 這樣的方法在語義上比catch / throw要好,但是很醜並且比它應該保持的更少。


你不想扔掉前 - 因為這會失去調用堆棧。 請參閱異常處理 (MSDN)。

是的,try ... catch沒有任何用處(除了失去調用堆棧 - 實際上更糟 - 除非由於某種原因你不想公開這些信息)。


大部分答案都是關於場景catch-log-rethrow。

考慮使用AOP,特別是帶有OnExceptionOptions IncludeParameterValue和IncludeThisArgument的Postsharp.Diagnostic.Toolkit


對不起,但許多“改進設計”的例子仍然聞到可怕或可能非常誤導。 嘗試{} catch {log; 扔}完全毫無意義。 異常日誌記錄應該在應用程序的中心位置完成。 無論如何,異常會觸發堆棧跟踪,為什麼不將它們記錄到某處並靠近系統邊界?

當您將上下文序列化(例如DTO)時,應該謹慎使用日誌消息。 它可以輕鬆包含敏感信息,而這些信息可能不希望傳達給所有可以訪問日誌文件的人員。 如果您沒有向異常添加任何新信息,我真的看不到異常包裝的重點。 良好的Java對此有一定的指導意義,它要求調用者知道應該期望什麼樣的異常,然後調用代碼。 由於.NET中沒有這個功能,所以在我見過的至少80%的案例中,包裝並沒有起到任何作用。


捕捉拋出的一個可能的原因是禁止任何從堆棧更深的過濾器( 隨機舊鏈接 )中的異常過濾器。 但是,當然,如果這是意圖,那麼會有評論說這樣說。


第一; 文章中的代碼的做法是邪惡的。 throw ex會將異常中的調用堆棧重置為該throw語句的位置; 失去了關於實際創建異常的信息。

其次,如果你只是捕獲並重新拋出,那麼我看不到任何附加價值,上面的代碼示例將會同樣好(或者,如果沒有嘗試捕獲,甚至更好)。

但是,有些情況下您可能想要捕捉並重新拋出異常。 記錄可能是其中之一:

try 
{
    // code that may throw exceptions    
}
catch(Exception ex) 
{
    // add error logging here
    throw;
}

重新拋出異常的一個合理原因可能是您想要將信息添加到異常中,或者可能將原始異常包裝在您自己製作的異常中:

public static string SerializeDTO(DTO dto) {
  try {
      XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
      StringWriter sWriter = new StringWriter();
      xmlSer.Serialize(sWriter, dto);
      return sWriter.ToString();
  }
  catch(Exception ex) {
    string message = 
      String.Format("Something went wrong serializing DTO {0}", DTO);
    throw new MyLibraryException(message, ex);
  }
}

除了其他人所說的之外,請參閱對相關問題的回答 ,它顯示捕獲和重新拋出不是無操作(它在VB中,但某些代碼可能是從VB調用的C#)。





try-catch