在 Delphi 中查找死代码通常非常简单:只需编译然后扫描缺少蓝点的例程。大多数时候,智能链接器非常擅长追踪它们。
问题是,这对事件处理程序不起作用,因为它们是已发布的方法,(理论上)可以通过 RTTI 以某种方式调用,即使这在实际实践中几乎从未发生过。
我正在尝试清理一个大型 VCL 表单单元,该单元在其历史上曾多次弯曲、折叠、旋转和肢解。如果我有办法找到表单的 DFM 实际未引用的事件处理程序并删除它们,那肯定会很好。有什么简单的方法可以做到这一点?例如,插件 IDE 专家?
在 Delphi 中查找死代码通常非常简单:只需编译然后扫描缺少蓝点的例程。大多数时候,智能链接器非常擅长追踪它们。
问题是,这对事件处理程序不起作用,因为它们是已发布的方法,(理论上)可以通过 RTTI 以某种方式调用,即使这在实际实践中几乎从未发生过。
我正在尝试清理一个大型 VCL 表单单元,该单元在其历史上曾多次弯曲、折叠、旋转和肢解。如果我有办法找到表单的 DFM 实际未引用的事件处理程序并删除它们,那肯定会很好。有什么简单的方法可以做到这一点?例如,插件 IDE 专家?
这有点难看(好吧,它非常难看),但对于一个单元来说,它几乎是万无一失的,并且不需要额外的工具:
使用“重命名方法”重构来重命名每个事件处理程序。选中“重构前查看引用”复选框。
检查重构窗口。如果事件处理程序链接到控件,将有一个“VCL 设计器更新”部分显示哪些控件链接到该方法。
这还将显示该方法是从任何其他单元调用的,还是以编程方式分配的。
注意:这是针对 D2006 的,后续版本可能会略有不同。
ModelMaker Code Explorer包含一个所谓的事件处理程序视图。它还显示未连接到任何组件的事件处理程序。
在最一般的情况下,没有任何解决方案可以保证给出正确的答案(如您所见,基于通过 RTTI 调用它们的可能性)。
一种解决方案是进行代码覆盖测试并仔细查看从未到达的处理程序。
我不知道有一个预先存在的应用程序或插件来执行此操作,但编写脚本应该不难。
假设您没有使用 RTTI 或手动分配事件处理程序:(我是 C++Builder 用户而不是 Delphi,因此以下内容可能不太正确。)
*.pas
. class
查找以声明或published
指令开头并以 、 或 结尾的每个end
文本private
块public
。在每个文本块中,提取每个procedure
.我最喜欢使用 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:
cleanup.sh
.cd /cygdrive/c/myapp
./cleanup.sh
并按 Enter。从自动的角度来看,我认为这是不可能的。当对象内部发生特定事件时,事件处理程序被激活。在给定的运行中未触发偶数并不意味着没有导致它的执行路径。
您还可以在运行时动态分配处理程序,因此在一种情况下使用的内容是不保证的。
例如
button.onclick := DefaultClickHandler;
button.onClick := SpecialClickHandler;
假设单击处理程序与 onclick 事件签名匹配,但如果签名不正确,您将不会获得编译。
但是,您可以通过查找具有 (Sender: TObject) 方法签名的所有方法并将他的方法与 .dfm 中的方法进行比较来找到所有废弃的处理程序(如果您是,请确保将其保存为文本)使用旧版本的delphi),在我的书中没有自动连接的antyhing会被怀疑。
--
如果您不想走 cygwin 路径,您可以将 src 和 dfm 加载到两个 TStirngLists 中,并从每个 TStirngLists 中取出名称/标识,并生成一个包含几个循环和一些字符串操作的列表。我的猜测是大约需要 20 分钟的工作才能得到可以忍受的东西。
有一个比克雷格更容易的方法。
转到可疑事件处理程序。以一致的方式重命名它——我通过在名称前面放一个 x 来做到这一点,然后继续执行并做同样的事情。看看编译器是怎么想的。
如果不满意,您只需将名称改回即可。
您可以使用相同的方法来消除不再执行任何操作的数据元素。