捕捉和抛出异常; 为什么这被认为是一种反模式

Car*_*uez 4 c# exception-handling

public void DeployCourse(Course course, Client client)
{
    if (course == null) throw new ArgumentNullException("Course cannot be null");       
    if (client == null) throw new ArgumentNullException("Client cannot be null");

    try
    {
        _ftp.Transfer(client.Server.IPAddress, course.PackageUrl, course.CourseName);
    }
    catch (Exception e)
    {
        var newException = new Exception(String.Format(
            "Error deploying Course: {0} to Client: {1}. See inner exception for more details", 
            course.CourseName, client.Name), e);
        throw newException;
    }
}
Run Code Online (Sandbox Code Playgroud)

我从来没有真正理解构成"好"异常处理的内容.一个快速的谷歌搜索显示,几乎一致,人们同意在调用堆栈深处捕获和重新抛出异常是不好的.上面,我有一些我正在写的代码的例子.这在典型的调用堆栈中非常低.我之所以这样做,是因为如果没有这段代码,很难找到无法部署的课程.我的问题是,如果我在许多(如果不是全部)方法中做了类似的事情,为异常添加更多上下文,为什么这会被视为反模式?

谢谢!

Eri*_*ert 11

这个问题有点模糊,所以让我们试着把它弄清楚一点.

是捕获异常的模式,将其包装在另一个异常中,并将新异常抛给调用者,这是一个好的模式吗?

是的,非常好.像任何模式一样,它有利有弊.

有什么优点?

当抛出的异常在逻辑上与调用者期望方法调用执行的操作相关时,它最有效.如果"操作Foo可以抛出FooFailedException"是记录合同的一部分,那么开发人员就知道他们需要抓住FooFailedException并且 - 这里只是 关键点FooFailedException.

如果您始终如一地应用此方法,则可以更改方法的实现详细信息,而不必担心会破坏调用方.

例如,如果您需要调用者捕获与FTP站点失败相关的异常,然后您更改实现以支持其他一些抛出不同异常的协议,则调用者也必须更改为抓住新的例外(或者他们必须抓住一切).使用此模式,无论实现细节是什么,调用者都可以捕获一个例外.

这也允许调用者根据他们尝试的操作获得诊断信息,而不是在失败的实现细节方面.

有什么缺点?

  1. 如果更改很晚,那么改变抛出的异常可能是一个突破性的变化.

  2. 调用者可能希望捕获更具体的基础异常.

  3. 如果由于您的代码有错误并且自己调用错误而引发了基础异常,那么您隐藏了您的错误并将其后果传递给调用者.

  4. 一些例外情况就像"网络现在正在关闭,稍后再试",一些例外就像"一切都很糟糕,你真的应该关闭这个过程".通过捕获所有内容并将其包装起来,您就更难以了解异常是否可以恢复.

  5. 我们有一份受黑客入侵的证书清单; 我们将列表保留在互联网上.如果证书在撤销证书列表中,我们希望不使用证书.快,找到缺陷:

    bool revoked = false;
    try
    {
        CheckTheRevocationList(out revoked);
    }
    catch(Exception ex)
    {
        new CryptoException("revocation list could not be checked", ex);
    }
    if (!revoked) UseTheCertificate();
    
    Run Code Online (Sandbox Code Playgroud)

看到缺陷?那是一个安全漏洞.在999的干草堆中发现该针对该模型的正确使用对人类来说是非常困难的.(这不是理论上的;我通过编写一个查找它的静态分析器在实际代码中找到了这个缺陷的一个版本.)

在哪里可以阅读有关异常处理模式和实践的更多信息?

我的文章在这里:

http://ericlippert.com/category/exception-handling/

您可能应该开始使用的那个经常在SO上引用:

http://ericlippert.com/2008/09/10/vexing-exceptions/