代码之家  ›  专栏  ›  技术社区  ›  Brann

您如何看待“不要捕捉意外异常”的最佳实践?

  •  7
  • Brann  · 技术社区  · 16 年前

    有人说,将Main()打包到catch块中以显示错误而不是退出是可以的( see this SO post for example ),但似乎有一个共识,即如果发生意外情况,你永远不应该让你的程序运行,因为它处于未知状态,可能会以意想不到的方式运行。

    虽然我同意隐藏bug而不是修复bug绝对是一个错误的想法,但请考虑以下情况:

    启动时,它将所有客户加载到其本地缓存中。

               foreach (string CustomerID in Customers)
                    try
                    {
                        LoadCustomer(CustomerID);
                    }
                    catch (Exception ex) // blind catch of all exceptions
                    {
                        // log the exception, and investigate later.
    
                    }
    

    如果没有盲目捕获,未能加载单个客户只会使所有服务器崩溃。

    我当然更喜欢让我的服务器启动,对一个客户产生一点未知的副作用,而不是让整个服务器停机。

    当然,如果我运行我的catch代码,我要做的第一件事就是修复代码中的错误。

    我在这里有什么东西在俯瞰吗?除了“从不捕捉意外异常”策略之外,是否有已知的最佳实践

    是否最好捕获LoadCustomer()方法中的异常,从那里重新抛出“CustomerLoadException”,并捕获CustomerLoadException而不是调用代码中的所有exception?

    14 回复  |  直到 8 年前
        1
  •  13
  •   Adam Bellaire    16 年前

    鲁棒性 ,即使出现无法解释的错误,它也会继续运行,vs 正确性 ,宁愿完全失败,也不愿产生不可靠的结果。

    显然,如果你正在研究生命支持系统,你不希望你的系统因为未知的错误情况而简单地关闭。即使你的状态没有很好地定义,但做好操作可能比终止要好。

    另一方面,如果你的程序是一个购物车,最好是完全失败,而不是可能发送错误的物品,或者向错误的人收取错误的金额。

    介于两者之间的一切都是灰色地带,这是一个判断。总的来说,生命支持系统编程似乎比购物车编程更为罕见,因此建议人们做最常见的事情,即在发生意外错误时失败。据了解,如果你正在处理一个不合适的案例(比如你的服务器示例),你会知道得更好。

        2
  •  2
  •   S.Lott    16 年前

    对。

    “除了‘从不捕捉意外异常’策略之外,是否有已知的最佳实践?”

    S. O. L. I. D. 原则。

    是否最好捕获LoadCustomer()方法中的异常,从那里重新抛出“CustomerLoadException”,并捕获CustomerLoadException而不是调用代码中的所有exception?

    loadCustomer

        3
  •  2
  •   Damien McGivern    16 年前

    我认为这取决于场景,但通常我只捕捉我希望在日常使用应用程序时发生的异常,并使用某种未受限制的异常报告/日志工具,如ASP的健康监控。网。但是,如果它是应用程序的关键部分,不能有未受限制的异常,我会捕获所有异常并再次报告/记录它们。

        4
  •  1
  •   Hank Gay    16 年前

    这是逐案处理的事情。如果你能保证加载失败不会对其他客户产生任何影响,那么记录并跳过该特定客户似乎是合理的。然而,并非所有的例外都是这样。特别是对于错误条件。, VirtualMachineError 应该 中止。

        5
  •  1
  •   Vatine    16 年前

    你怎么知道导致LoadCustomer()中异常的错误没有影响到其他任何东西?总的来说,我认为我更喜欢“记录异常,将异常重新抛出到更高级别”,然后可能退出。

    不过,在这个特定的情况下,我可能会支持两种处理方式,但总的来说,处理你没有明确处理方式的错误并不理想。

        6
  •  1
  •   Greg    16 年前

    如果网页抛出未经处理的异常,您会想到“meh”并点击刷新。如果桌面应用程序抛出一个,你可能会丢失一些数据,并感到非常恼火。如果服务器抛出一个问题,你的整个业务可能会陷入瘫痪,直到问题得到解决。

    这也取决于补丁的容易程度——如果你很容易将补丁推送到所有安装(比如你自己的服务器或内部应用程序),那么最好不捕获异常。如果你正在编写无法修补的东西(适用于旧主机或嵌入式设备应用程序的游戏),那么你最好接受这个异常。

        7
  •  1
  •   kyoryu    16 年前

    捕捉你所知道的异常是完全可以的。您可能会在示例中遇到NullObjectException。抓住这一点是完全合理的。

    当你捕获通用异常时,你基本上是在说“我不知道出了什么问题,或者服务器是否着火了 我不在乎 “虽然您的服务器可能会继续运行,但无法确定内部状态,也无法确定此异常是否会导致未来崩溃。

    此外,通过在此时捕获任意异常,如果稍后发生崩溃,您将离“真实”错误发生的地方更远,这将使查找错误变得更加困难。

    同样,虽然不捕获通用异常可能会导致立即崩溃,但这种崩溃更有可能在开发周期的早期被捕获,并且很容易修复(因为你会知道错误实际发生在哪里)。

    捕获Exception真的没有好处。

        8
  •  0
  •   batbrat    16 年前

    我在这方面经验不多。然而,在我看来: 1.几乎任何规则都有例外。知道什么时候打破规则很重要。 2.在异常处理的情况下,盲目捕捉异常几乎从来都不是一个好主意。这是因为您可能会发现一些真正意想不到的错误。

    例如,在Python(至少2.5.2)中,盲目捕获将允许我捕获ctrl+c信号(Linux)。这意味着我不能在紧急情况下终止申请。

    1. 对于您的Web服务器代码,您可能需要执行以下操作之一
      +使用自定义异常,如果客户未能加载并记录/纠正该异常,则会抛出该异常。
      +在代码的更深层次使用异常处理,并在那里进行处理。
        9
  •  0
  •   Robert Gould    16 年前

    好吧,让我这么说。.say神秘的客户未能加载,因为它不存在,比如说因为有人想出了如何破解你系统的前几层。现在想象一下,黑客用不存在的力量做各种恶意的事情,直到他碰巧触发了一些试图从未知客户值中读取的功能,现在系统无论如何都会崩溃,但黑客本可以对无效客户进行各种破坏。

    这真的更好吗?

        10
  •  0
  •   Brian    16 年前

    为了解决真正会损害我的程序稳定性的异常,也许写这种代码是个好主意:

          foreach (string CustomerID in Customers)
                try
                {
                    LoadCustomer(CustomerID);
                }
                catch (Exception ex) // blind catch of all exceptions
                {
                    if (ex is OutOfMemoryException || ex is StackOverflowException ||...)
                         throw;
                    // log the exception, and investigate later.
    
                }
    
        11
  •  0
  •   Jens Schauder    16 年前

    当您知道哪些对象和资源可能会受到影响并丢弃所有这些对象时,您可以很好地捕获所有异常(但不是错误)。

    所以,就你而言 可能 可以很好地处理异常,将整个过程转换为noop。但你必须确保: -其他共享资源不受影响(例如,在异常后可能已死亡的Hibernate会话) -一个完整的(转换)操作不仅被取消了一半。这意味着这种异常处理可能只会出现在特殊的地方,通常直接出现在用户界面的“下方”。示例:用户按下一个按钮,该按钮应该加载一个客户,更改其地址并再次保存。当其中任何一个出错时,您可能会捕获异常,阻止所有剩余步骤的发生,丢弃客户对象并显示一条消息:对不起,不起作用。

    但是,如果你捕捉到异常(例如在修改地址期间),然后继续工作,用户会控制一个地址已更改的客户对象,而实际上他控制的是一个地址被销毁的客户对象。情况不妙。

        12
  •  0
  •   Matt Warren    16 年前

    我以前被抓到的是一个通用的“异常”,而不是单个类型。这意味着你失去了一些关于实际问题的信息。您通常希望以不同的方式处理每种类型的异常。

    此外,顶级异常处理应仅用于记录错误并重新启动应用程序。如果你不知道是什么类型的异常导致了错误,你的程序可能处于无效状态,你可能会遇到内存不足或其他问题。

        13
  •  0
  •   Esteban Küber    16 年前

    这取决于你站在哪里。如果你处于较低的水平,你可以预见到大多数异常,你应该明确地只捕捉到这些异常,让其余的出现。当你进入GUI或顶层逻辑时,所有已知的异常都会被捕获,你最终只会得到 意外的 例外情况。现在你有两个选择:

    1. 悲观观点:显示例外,放弃。
    2. 乐观的观点:你有信心你已经抓住了所有批评的例外,所以你放弃了例外并继续( ON ERROR RESUME NEXT 有人吗?)

    无论哪种方式,您都要记录异常并确保定期检查日志。

    最终,这是一个“一切取决于”的局面。更糟糕的是什么?继续使用坏数据还是意外退出?这是一个关键系统吗?它的输出是否被另一个系统使用?等等。

    不管怎样,所有关键异常 应该 在应用程序的更深层次上被抓住了。

        14
  •  0
  •   supercat    14 年前

    许多语言中异常层次结构的一个主要问题是,异常是按类型或按调用类组织的,而不是按严重性组织的。当抛出异常时,通常是因为一个方法在做任何事情之前就确定其先决条件未得到满足。抛出异常的第一个例程的调用者在这一点上可能做了也可能没有做任何重要的事情。在许多情况下,调用者在调用例程之前所做的任何操作都只会对临时变量起作用,当异常出现时,这些临时变量就会消失。因此,在绝大多数情况下,当LoadDocument()例程抛出异常时,加载文档的尝试对仍然存在的任何东西的状态都没有影响。如果加载文档的失败尝试没有副作用,并且应用程序已准备好处理文档未加载的事实,则应用程序没有理由不继续。困难在于知道是否有任何副作用。

    我认为现有异常处理系统缺少的一个功能是exception中的虚拟IsResolved属性。处理“catch”块后,系统将检查IsResolved是否返回false。如果它不是null并且返回true,则将调用UnresolvedExceptionException,并将前一个异常作为其InnerException(UnresolvedExcelmentException.IsResolved将调用其InnerException的IsResolved方法)。

    大多数IsResolved实现都会测试返回布尔值的函数的委托列表是否为空;如果没有,如果列出的任何委托返回true,则返回true。如果异常意味着未处理的对象已损坏,则IsResolved方法可以返回该对象的“已处理”标志。如果该对象已被处置,则其损坏将不再相关。如果对象被包装在Using块中,这种行为将导致异常渗透到对象被处置的点,但不会进一步渗透。

    不幸的是,我不确定这样的方案能添加到像.net这样的现有平台上有多好。尽管如此,如果将来有人开发出类似的系统,它似乎是一个有用的功能。