【问题标题】:what can't be done with if else clause, and can be done with exception handling?if else 子句不能做什么,异常处理可以做什么?
【发布时间】:2011-06-08 05:57:21
【问题描述】:

if else子句不能做什么,异常处理可以做什么?

换句话说,我们实际上需要在哪里使用异常处理,而仅凭 if else 将无法达到目的。

异常处理只是显示错误的一种美化方式吗?

【问题讨论】:

  • if-else 可以完成哪些 goto 无法完成的任务? if-else 分支只是编写有限的一组gotos 的一种美化方式吗?
  • @pst:例外情况并非如此。
  • @sharptooth 我认为我的观点被忽略了...... :)

标签: exception-handling


【解决方案1】:

在最早的 C++ 实现中,异常被编译成等效的 if/else 结构,或多或少。而且它们像糖蜜一样慢,以至于您仍然可以找到以“它们很慢”为由建议反对异常的编程指南。

所以这不是“可以”或“不能”的问题。这是可读性和性能的问题。

用错误检查乱扔代码的每一行会使人更难遵循非异常(即常见)情况。每个优秀的程序员都知道,您的代码的主要受众是人类读者。

就性能而言,if/else 比现代异常实现。现代实现几乎为零开销,除非实际抛出异常。如果您的异常确实代表“异常”情况,那么这可能是一个显着的性能差异。

【讨论】:

    【解决方案2】:

    某些语言(例如 C++)在某些情况下不允许返回值。例如,在 C++ 中,构造函数没有返回值(甚至 void),因此在构造函数中发出错误信号的唯一方法是抛出异常。

    【讨论】:

    • 这并不意味着没有例外就不可能处理它,例如,您可以在字段中创建一个带有isValid 标志的对象。
    • @trutheality:谁必须检查那个标志?
    • 当然是每个尝试使用该对象的人。我没有说它漂亮,只是它不是不可能的。
    • 尽可能避免两者。除非例外,否则避免例外。如果可以避免也不想检查状态。构造函数应该返回一个“有效”实例,我的规则是一个具有有效关联图的对象。我在构造函数中避免了很多其他事情,我认为有些事情本质上应该永远不会失败。我的构造函数需要有效的输入,句号。如果某些事情需要进一步的初始化/验证,返回值仍然只有在用户可以提供一些修复时才有用。所以一个显式的初始化函数调用是合适的——用户必须准备好参与进来。
    【解决方案3】:

    你看,你不能做任何你不能用手工编码的 8086 汇编器做的例外。 (图灵完备!)除了手动编码的汇编器对于 10 万行代码项目来说不是一个很好的工具,除非你是银河系中最好的编码器,如果那样的话。经验表明,在许多情况下,异常处理的习语会产生由普通人编写的最健壮的代码。 Sharptooth 的 ctor 示例很好。需要向上传播调用堆栈的错误也是如此。 编辑:实际上有多少人检查malloc 没有每次都失败?最好抛出 OutOfMemoryException。

    【讨论】:

      【解决方案4】:

      异常就是它们的名称:打破当前流程的异常事件或情况。

      您应该使用异常来表示发生了令人讨厌且非常异常的事情。假设您有一个读取配置文件的类。特殊情况可能是:

      • 找不到文件。
      • 文件存在,但无法读取。
      • 文件在中途被删除 读取操作。

      可以使用 if-else 块处理所有这些问题,但处理异常要简单得多。

      【讨论】:

        【解决方案5】:

        当然,您可以编写不使用异常的代码。但是,如果您这样做,您将确保正确处理任何可能发出错误的函数。

        对我来说,异常的最大好处是我可以编写简单的代码,假设所有函数都成功,知道上层会处理报告错误。

        具体来说,采用以下虚构函数在编辑器中处理文本:

        do-stuff:
            backward-line 10
            x = point
            search-for "FOO"
            return buffer-substring x point
        

        “backward-line”和“search-for”都可能失败。如果我必须自己处理错误,我将不得不检查它们。此外,我必须发明一个侧通道来向我的调用者报告发生了错误。正如我所说,它可以完成,但会更麻烦。

        【讨论】:

          【解决方案6】:

          考虑以下现实生活中的示例。您有一个复杂的递归函数,需要在某些情况下退出当前正在进行的所有调用。

          void complexFunction()
          {
             //do stuff, then
             if( timeToBailOut() ) {
                // what? how would you get out of all instances at once?
             }       
          }
          

          您需要非常仔细地编写那些if 语句,也许会引入一个特殊的返回值,并始终检查该值,这会使您的代码变得非常复杂。您可以轻松实现该行为,但有例外。

          if( timeToBailOut() ) {
              throw BailOutException();
          }
          

          【讨论】:

          • 可以说,它可以像返回Either(Result,Error)一样干净利落。当我遇到这种预期的“退出”方法时,我通常会哭。
          • @pst:Either() 会做什么?
          • @sharptooth 它可以被认为是一个具有两个互斥元素的元组,其中可以设置(或访问)一个且只有一个项。 要么第一个值——通常称为Left——可以设置,或者第二个值——通常称为Right——可以设置。在上面的评论中,我的意思是左类型为结果,右类型为错误。例如。正常返回是Left(some_result),“错误”返回是Right(some_error),它们都统一在Either(Result,Error)下。通常模式匹配用于分解为 Left 或 Right 值。
          • @pst:好的,但是调用者必须检查该值并做出相应的反应,这将是额外的代码。除了例外情况,它只是顶层的catch
          • @sharptooth 争论是 BailOutException 是一种滥用——它是一个预期条件,因此可以被编码——而不是像 IO 或内存异常之类的东西.
          【解决方案7】:

          没有异常处理就无法完成任何事情,但是,如果没有异常处理,有些情况会很痛苦。

          示例:a() 调用 b()b() 调用 c()c() 中可能发生错误,必须在 a() 中处理。如果没有异常处理,您将不得不使用b()c() 中的标记返回值以及b() 中的额外检查代码来处理它。您可以想象,如果错误必须通过更多级别(从导致错误的原因到需要处理错误的级别),您需要编写多少代码。

          这是我对 different question 的回答的简短版本,之前在此处询问并已迁移。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-10-08
            • 2014-06-20
            • 1970-01-01
            • 2015-02-18
            • 2022-01-08
            • 1970-01-01
            • 2012-04-28
            • 2012-11-17
            相关资源
            最近更新 更多