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

urt*_*let 5 delphi

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

Description := IfThen(Assigned(Widget), Widget.Description, 'No Widget');
Run Code Online (Sandbox Code Playgroud)

我希望当Widget为零时会崩溃,但是当我对其进行测试时,它运行良好。

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

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

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

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

Ken*_*ssa 7

就个人而言,我讨厌依赖于非契约行为。

内联指令是对编译器的建议。

如果我正确理解所读内容,则使用运行时程序包进行构建时,代码也会崩溃。

内联永远不会跨越包边界发生

就像乌利·格哈特(Uli Gerhardt)所评论的那样,它首先可以被认为是一个臭虫。由于行为不是契约性的,因此可以随时更改。

如果我有任何建议,我将其标记为低优先级“修复”。我敢肯定,有人会争辩说,如果代码有效,则不需要修复,就不会有bug。在那一点上,它更多地是一个哲学问题(如果一棵树掉在森林里,没人在周围听到它,它会发出声音吗?


Dav*_*nan 5

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

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

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

为了扩展第二点,请考虑您的问题中的陈述:

Description := IfThen(Assigned(Widget), Widget.Description, 'No Widget');
Run Code Online (Sandbox Code Playgroud)

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

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

因此,有了这些知识,您可以通过以下几种方法使代码失败:

  1. 您转到运行时包,否则编译器决定不内联该函数。
  2. 您将Description属性获取器从字段获取器更改为函数获取器。

如果是我,我不想依靠这种行为。但是正如我在最上面说的那样,最终由您决定。

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