6

在 Delphi 中查找死代码通常非常简单:只需编译然后扫描缺少蓝点的例程。大多数时候,智能链接器非常擅长追踪它们。

问题是,这对事件处理程序不起作用,因为它们是已发布的方法,(理论上)可以通过 RTTI 以某种方式调用,即使这在实际实践中几乎从未发生过。

我正在尝试清理一个大型 VCL 表单单元,该单元在其历史上曾多次弯曲、折叠、旋转和肢解。如果我有办法找到表单的 DFM 实际未引用的事件处理程序并删除它们,那肯定会很好。有什么简单的方法可以做到这一点?例如,插件 IDE 专家?

4

7 回答 7

6

这有点难看(好吧,它非常难看),但对于一个单元来说,它几乎是万无一失的,并且不需要额外的工具:

  1. 确保将当前版本的表单签入源代码管理!
  2. 转到事件处理程序所在类的接口顶部。删除所有事件处理方法接口。
  3. 查看代码资源管理器/错误洞察。有实现但没有接口的方法将被突出显示。删除实现。
  4. 现在保存单元。Delphi 将一次一个地抱怨每个实际处理的事件缺少事件处理程序。当错误出现时把这些写下来。
  5. 查看表单的原始版本,并删除任何不在您列表中的事件处理程序。
于 2009-03-26T21:32:39.713 回答
6

使用“重命名方法”重构来重命名每个事件处理程序。选中“重构前查看引用”复选框。

检查重构窗口。如果事件处理程序链接到控件,将有一个“VCL 设计器更新”部分显示哪些控件链接到该方法。

这还将显示该方法是从任何其他单元调用的,还是以编程方式分配的。

注意:这是针对 D2006 的,后续版本可能会略有不同。

于 2009-03-26T22:47:56.803 回答
3

ModelMaker Code Explorer包含一个所谓的事件处理程序视图。它还显示未连接到任何组件的事件处理程序。

于 2011-09-13T19:39:56.407 回答
2

在最一般的情况下,没有任何解决方案可以保证给出正确的答案(如您所见,基于通过 RTTI 调用它们的可能性)。

一种解决方案是进行代码覆盖测试并仔细查看从未到达的处理程序。

于 2009-03-26T21:21:57.243 回答
2

我不知道有一个预先存在的应用程序或插件来执行此操作,但编写脚本应该不难。

假设您没有使用 RTTI 或手动分配事件处理程序:(我是 C++Builder 用户而不是 Delphi,因此以下内容可能不太正确。)

  1. 列出代码中所有已发布的方法。
    • 正确的方法是阅读*.pas. class查找以声明或published指令开头并以 、 或 结尾的每个end文本privatepublic。在每个文本块中,提取每个procedure.
    • 执行此操作的简单方法是列出常见事件处理程序类型并假设它们已发布。
  2. 获得此列表后,打印列表中未在 DFM 文件中找到的所有内容。

我最喜欢使用 Cygwin 或 Linux 工具编写脚本。这是一个在 Cygwin 中工作的 bash 脚本,应该做你想做的事。

#!/bin/bash

for file in `find -name *.pas`; do
    echo $file:

    # Get a list of common event handling procedures.
    # Add more types between the | symbols.
    egrep '^[[:space:]]+procedure.*(Click|FormCreate|FormClose|Change|Execute)\(' $file | 
    awk '{print $2}' | cut -f 1 -d '(' > published.txt

    # Get a list of used event procedures.
    egrep '^[[:space:]]+On.* =' ${file%.pas}.dfm | awk '{print $3}' > used.txt

    # Compare the two.
    # Files listed in the left column are published but not used, so you can delete them.
    # Files in the right column were not by our crude search for published event 
    # handlers, so you can update the first egrep command to find them.
    comm -3 published.txt used.txt

    echo

done

# Clean up.
rm published.txt used.txt

要实际使用它,如果您不熟悉 Cygwin:

  • 下载并安装 Cygwin。我认为默认安装应该为您提供我使用的所有工具,但我并不肯定。
  • 将脚本另存为cleanup.sh.
  • 启动 Cygwin 命令提示符。
  • 如果您的源位于 c:\MyApp 中,则键入cd /cygdrive/c/myapp
  • 键入./cleanup.sh并按 Enter。
于 2009-03-26T21:36:44.150 回答
2

从自动的角度来看,我认为这是不可能的。当对象内部发生特定事件时,事件处理程序被激活。在给定的运行中未触发偶数并不意味着没有导致它的执行路径。

您还可以在运行时动态分配处理程序,因此在一种情况下使用的内容是不保证的。

例如

button.onclick := DefaultClickHandler;

button.onClick := SpecialClickHandler;

假设单击处理程序与 onclick 事件签名匹配,但如果签名不正确,您将不会获得编译。


但是,您可以通过查找具有 (Sender: TObject) 方法签名的所有方法并将他的方法与 .dfm 中的方法进行比较来找到所有废弃的处理程序(如果您是,请确保将其保存为文本)使用旧版本的delphi),在我的书中没有自动连接的antyhing会被怀疑。

--

如果您不想走 cygwin 路径,您可以将 src 和 dfm 加载到两个 TStirngLists 中,并从每个 TStirngLists 中取出名称/标识,并生成一个包含几个循环和一些字符串操作的列表。我的猜测是大约需要 20 分钟的工作才能得到可以忍受的东西。

于 2009-03-26T21:43:59.467 回答
2

有一个比克雷格更容易的方法。

转到可疑事件处理程序。以一致的方式重命名它——我通过在名称前面放一个 x 来做到这一点,然后继续执行并做同样的事情。看看编译器是怎么想的。

如果不满意,您只需将名称改回即可。

您可以使用相同的方法来消除不再执行任何操作的数据元素。

于 2009-03-26T22:16:51.073 回答