C# 处理来自可能引发多个异常的方法的异常

C# 处理来自可能引发多个异常的方法的异常,c#,exception,exception-handling,C#,Exception,Exception Handling,我有一个API类,使用and类将字符串压缩和解压缩为字节数组 使用这两个类可能会抛出许多异常,我想知道对于API来说,处理抛出异常的最佳方法是什么。在这种情况下,最好用我自己的异常包装每个异常,还是最好在调用代码中捕获每个单独的异常 我认为这是一个不局限于这个特定用例的问题,而是关于一般异常处理最佳实践的问题 /// <summary> /// Compress the string using the SharpLibZip GZip compression routines //

我有一个API类,使用and类将字符串压缩和解压缩为字节数组

使用这两个类可能会抛出许多异常,我想知道对于API来说,处理抛出异常的最佳方法是什么。在这种情况下,最好用我自己的异常包装每个异常,还是最好在调用代码中捕获每个单独的异常

我认为这是一个不局限于这个特定用例的问题,而是关于一般异常处理最佳实践的问题

/// <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);
    }
}
//
///使用SharpLibZip GZip压缩例程压缩字符串
/// 
///要压缩的字符串对象
///传入字符串的GZip压缩字节数组
///压缩内存流失败时抛出
///字符串参数为空时抛出
///字符串参数为空时抛出
公共异步任务CompressStringAsync(字符串s)
{
如果(s==null)抛出新的ArgumentNullException(“s”);
if(string.IsNullOrWhiteSpace)抛出新的ArgumentException(“s”);
字节[]压缩=空;
尝试
{
使用(MemoryStream outStream=新MemoryStream())
{
使用(GZipStream tinyStream=新的GZipStream(扩展,压缩模式.Compress))
{
使用(MemoryStream memStream=new MemoryStream(Encoding.UTF8.GetBytes)))
{
等待memStream.CopyToAsync(tinyStream);
}
}
压缩=超出流。ToArray();
}
返回压缩;
}
捕获(ArgumentNullException-ex)
{
抛出新的StringCompressionException(“参数为Null”,ex);
}
捕获(EncoderFallbackException ex)
{
抛出新的StringCompressionException(“流编码失败”,ex);
}
捕获(参数异常)
{
抛出新的StringCompressionException(“参数无效”,ex);
}
捕获(ObjectDisposedException ex)
{
抛出新的StringCompressionException(“已处理流”,ex);
}
捕获(不支持例外)
{
抛出新的StringCompressionException(“不支持操作”,ex);
}
}

这是一篇关于捕获基本异常的好文章。

您已经在展示的示例代码中利用了
异常
基类的优点,为什么不一直使用它呢

您不需要重新编写内部异常的消息,只需将异常包装在
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);
}

还要记住,异常消息字符串不容易本地化,因此无论如何,它们都不应该显示给最终用户。它们实际上只是一个调试辅助工具,所以类似于“查看内部异常以了解详细信息”的功能很好。维护程序员知道该怎么做。

通常我会这样做

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

这允许您在每种情况下执行相关操作(即请求新参数)并酌情处理/抛出。

始终建议您处理特定异常,然后在最后处理一般异常。此外,决策取决于您计划在收到特定异常时实施的操作类型。从您的代码来看,您似乎正在从所有特定异常引发相同的异常,因此,除非您希望在该特定块中执行任何特定操作,否则我将使用带有基本异常的catch块或简单的catch


还有一点需要记住的是,抛出新的xxx将重置堆栈跟踪,因此您可能会错过一些生成异常的重要信息。

捕获基本异常类不是普遍不赞成吗?代码分析工具倾向于抱怨它。@Phil是的,通常是这样,因为当你这么做的时候,你也会发现不可恢复的错误,比如OutOfMemoryException。您应该只捕获准备处理的异常。但在本例中,您只是将底层异常包装到一个特定的API中,然后重新引用,所以它可能没问题。除非执行原始代码所做的操作:选择单个异常,否则您没有太多其他选择。我认为.NET中的异常层次结构设计得不太好。他抛出的新异常对象将原始异常作为其内部异常,因此没有丢失任何信息的风险。我看到了,我只是对标准实践进行了评论。:),谢谢你指出。一个人如何“要求新的论点”?您的代码在传递无效参数时除了抛出异常之外是否还做了其他事情?