【问题标题】:IfThen(Assigned(Widget), Widget.Description, 'No Widget') doesn't crash. Should it?IfThen(Assigned(Widget), Widget.Description, 'No Widget') 不会崩溃。应该是?
【发布时间】:2017-09-17 15:17:44
【问题描述】:

在我帮助维护的代码中,我发现了多个如下代码示例:

Description := IfThen(Assigned(Widget), Widget.Description, 'No Widget');

我预计当 Widget 为 nil 时它会崩溃,但当我测试它时,它运行良好。

如果我在项目 - 选项 - 编译器中关闭“代码内联控件”重新编译它,我确实会遇到访问冲突。

看来,因为 IfThen 被标记为内联,如果 Widget 为 nil,编译器通常不会评估 Widget.Description。

有什么理由应该“修复”代码,因为它似乎没有被破坏?他们不希望不必要地更改代码。 它会咬它们吗?

我已经用 Delphi XE2 和 XE6 对其进行了测试。

【问题讨论】:

  • 这确实是一个基于意见的问题。由你决定。有些人会改变它,有些人不会。
  • 谢谢@David 和 Victoria 如果没有充分的理由改变它,他们宁愿不改变。他们不太可能更改内联选项。
  • 这不是编译器错误吗?内联不应该改变可观察到的行为。但显然它适用于IfThen。比如x := IfThen(SomeCondition, GetXWithSideEffect1, GetXWithSideEffect2);
  • @Uli,你为什么这么想?这里没有额外的魔法。如果您不内联该函数,则会在将所有参数传递给函数之前对其进行评估。如果这样做,内联函数会在需要时在内联代码中“评估它们”。
  • 例如在 C++ 中内联不允许改变 obersvable 行为。我确实认为这是一个错误。事实上,我对此感到震惊,我花了一段时间才吸收这个事实。我一直相信内联不会改变可观察到的行为。回到绘图板。

标签: delphi


【解决方案1】:

就个人而言,我讨厌依赖非合同行为。

The inline directive is a suggestion to the compiler.

如果我理解正确,如果您使用运行时包构建,您的代码也会崩溃。

内联永远不会跨越包边界

就像 Uli Gerhardt 评论的那样,它可以被认为是一个错误,它首先可以工作。由于该行为不是合同行为,因此可以随时更改。

如果我要提出任何建议,我会将其标记为低优先级“修复”。我很确定有些人会争辩说,如果代码有效,它就不需要修复,没有错误。到那时,它就变成了一个哲学问题 (If a tree falls in a forest and no one is around to hear it, does it make a sound?)

【讨论】:

    【解决方案2】:

    有什么理由应该“修复”代码,因为它似乎没有被破坏?

    这确实是一个只有你才能回答的问题。但是,要回答这个问题,您需要充分理解依赖这种行为的含义。我认为有两个主要问题:

    1. 不保证函数的内联。编译器可能会选择不内联,对于运行时包或 DLL,不能内联另一个包中的函数。
    2. 仅当编译器确定没有与参数计算相关的副作用时,才会跳过对参数的计算。例如,如果参数涉及函数调用,编译器将确保始终对其进行评估。

    要扩展第 2 点,请考虑您问题中的陈述:

    Description := IfThen(Assigned(Widget), Widget.Description, 'No Widget');
    

    现在,如果 Widget.Description 是一个字段,或者是一个带有读取字段的 getter 的属性,那么编译器会确定评估没有副作用。可以安全地跳过此评估。

    另一方面,如果Widget.Description 是一个函数,或带有getter 函数的属性,则编译器确定可能存在副作用。因此,它确保 Widget.Description 只被评估一次。

    因此,有了这些知识,以下是您的代码失败的几种方法:

    1. 您转向运行时包,或者编译器决定不内联函数。
    2. 您将Description 属性getter 从字段getter 更改为函数getter。

    如果是我,我不想依赖这种行为。但正如我在顶部所说的,最终由您决定。

    最后,行为已从 XE7 更改。内联函数的所有参数只计算一次。这与其他语言保持一致,意味着可观察行为不再受内联决策的影响。我会将 XE7 中的更改视为错误修复。

    【讨论】:

      【解决方案3】:

      它已经在 XE7 中得到修复,并确认这应该是错误的行为。

      https://quality.embarcadero.com/browse/RSP-11531

      【讨论】:

      • 这可能会改变对这种情况的适当响应。我找到了一台 Delphi 10 Seattle 机器,并确认即使打开内联,代码也会出现访问冲突。所以如果软件移动到最新版本的Delphi,就会崩溃。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-15
      相关资源
      最近更新 更多