2013-07-11 61 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