2013-07-11 60 views
2

我有一個使用MemoryStreamGZipStream類的API類將字符串壓縮並解壓縮爲字節數組。處理可能引發多個異常的方法的異常

使用這兩個類可能會拋出一些異常,我想知道處理拋出的API異常的最佳方法是什麼。在這種情況下,使用我自己的Custom Exception來包裝每個異常還是更好一些,還是最好在調用代碼中捕獲每個異常?

我想這是一個問題,不僅限於這個特定的用例,更多的是關於一般異常處理的最佳實踐。

/// <summary> 
/// Compress the string using the SharpLibZip GZip compression routines 
/// </summary> 
/// <param name="s">String object to compress</param> 
/// <returns>A GZip compressed byte array of the passed in string</returns> 
/// <exception cref="Helper.Core.Compression.StringCompressionException">Throw when the compression memory stream fails </exception> 
/// <exception cref="System.ArgumentNullException">Throw when string parameter is Null</exception> 
/// <exception cref="System.ArgumentException">Throw when the string parameter is empty</exception> 
public async Task<byte[]> CompressStringAsync(string s) 
{ 
    if (s == null) throw new ArgumentNullException("s"); 
    if (string.IsNullOrWhiteSpace(s)) throw new ArgumentException("s"); 

    byte[] compressed = null; 

    try 
    { 
     using (MemoryStream outStream = new MemoryStream()) 
     { 
      using (GZipStream tinyStream = new GZipStream(outStream,CompressionMode.Compress)) 
      { 
       using (MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(s))) 
       { 
        await memStream.CopyToAsync(tinyStream); 
       } 
      } 
      compressed = outStream.ToArray(); 
     } 
     return compressed; 
    } 
    catch (ArgumentNullException ex) 
    { 
     throw new StringCompressionException("Argument Was Null", ex); 
    } 
    catch (EncoderFallbackException ex) 
    { 
     throw new StringCompressionException("Stream Encoding Failure", ex); 
    } 
    catch (ArgumentException ex) 
    { 
     throw new StringCompressionException("Argument Was Not Valid", ex); 
    } 
    catch (ObjectDisposedException ex) 
    { 
     throw new StringCompressionException("A Stream Was Disposed", ex); 
    } 
    catch (NotSupportedException ex) 
    { 
     throw new StringCompressionException("Action Was Not Supported", ex); 
    } 
} 

Here是一個很好的發現基地例外。

回答

2

您已經在示例代碼中利用了Exception基類的"inner exception" functionality優勢,那麼爲什麼不把它放在一邊呢?

您不需要重寫內部異常的消息,只需在您的StringCompressionException類中包裝異常即可。如果捕獲異常的客戶想要了解有關失敗的更多詳細信息,他們可以檢查內部異常。

,使你的代碼變得簡單許多:

try 
{ 
    using (MemoryStream outStream = new MemoryStream()) 
    { 
     using (GZipStream tinyStream = new GZipStream(outStream,CompressionMode.Compress)) 
     { 
      using (MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(s))) 
      { 
       await memStream.CopyToAsync(tinyStream); 
      } 
     } 
     compressed = outStream.ToArray(); 
    } 
    return compressed; 
} 
catch (Exception ex) 
{ 
    throw new StringCompressionException("Compressing string failed; see inner exception for details.", ex); 
} 

而且要記住,異常消息字符串不容易定位,所以他們不是說你本來也應該顯示給最終用戶的東西。它們實際上只是一個調試幫助,所以像「查看細節的內部異常」一樣正常。維護程序員知道該怎麼做。

+0

是不是普遍皺起了眉頭趕上基地的異常類?代碼分析工具傾向於對此抱怨。 –

+0

@Phil是的,它通常是,因爲當你這樣做的時候,你也會捕獲像'OutOfMemoryException'這樣的不可恢復的錯誤。你應該只捕捉你準備處理的異常。但是在這種情況下,你只是將基礎異常封裝在一個特定的API中,然後重新投射,所以它可能是好的。除非您執行原始代碼的操作,否則您沒有其他選擇:挑選單個的異常。 .NET中的異常層次結構設計不是很好,我不這麼認爲。 –

0

一般來說,我會做它像這樣

try 
{ 
    CodeThatThrowsExceptions(); 
} 
catch(ArgumentException ae) 
{ 
    //Recoverable, request new args 
} 
catch(OtherRecoverableException oe) 
{ 
    //take appropriate action 
} 
catch(Exception e) 
{ 
    //Unexpected irrecoverable error. Handle appropriately 
} 

這允許你做的是在每種情況下(即要求新參數)和相關處理/適當拋出。

+0

如何「請求新論據」?當你的代碼通過無效參數傳遞時,你的代碼是否會執行一些其他的異常? –

0

總是建議您處理特定異常,然後處理最後的一般異常。此決定還取決於您計劃在收到特定例外情況時採取的行動。從你的代碼看,你從所有特定的異常中產生了相同的異常,所以除非你想在特定的塊中採取任何特定的操作,否則我會在catch塊上使用基本異常或者簡單地捕獲。

也談要記住的事情是拋出新的XXX重置堆棧跟蹤,所以你可能會錯過從那裏已經產生了異常的一些重要信息。

+0

他拋出的新異常對象包含原始內部異常,因此不會有丟失任何信息的風險。 –

+0

我明白了,我只是在評論標準做法。 :),謝謝指出。 – seshuk