IfThen(Assigned(Widget),Widget.Description,'No Widget')不会崩溃。应该是?

时间:2017-09-14 13:52:02

标签: delphi

在我帮助维护的代码中,我找到了多个代码示例:

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

我预计当Widget为零时会崩溃,但是当我测试它时,它运行得很好。

如果我在Project - Options - Compiler中关闭“Code inlining control”重新编译它,我会收到访问冲突。

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

是否有任何理由认为代码应该“固定”,因为它似乎没有被破坏?他们不希望代码不必要地改变。 是否有可能咬他们?

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

3 个答案:

答案 0 :(得分:7)

就个人而言,我讨厌依赖一种不合同的行为。

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?

答案 1 :(得分:5)

  

是否有任何理由认为代码应该被修复",因为它似乎没有被破坏?

这真的是一个只有你能回答的问题。但是,要回答它,您需要充分了解依赖此行为的含义。我认为有两个主要问题:

  1. 无法保证内联功能。编译器可能选择不内联,并且在运行时包或DLL的情况下,不能内联另一个包中的函数。
  2. 仅当编译器确定没有与参数评估相关的副作用时,才会跳过对参数的评估。例如,如果参数涉及函数调用,则编译器将确保始终对其进行求值。
  3. 要扩展第2点,请考虑您问题中的陈述:

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

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

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

    因此,掌握了这些知识,以下几种方法可以使您的代码失败:

    1. 您转到运行时包,或者编译器决定不内联函数。
    2. 您将Description属性getter从字段getter更改为函数getter。
    3. 如果是我,我不想依赖这种行为。但正如我在顶部说的那样,最终是你的决定。

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

答案 2 :(得分:4)

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

请参阅https://quality.embarcadero.com/browse/RSP-11531