在 Delphi 中删除表单上某个类的所有组件

12

这可能是一个愚蠢的问题,但我的大脑已经够累了,我想我要使用我的“生命线”之一,看看能否从我的stack overflow朋友那里得到帮助。 ;)

我需要删除主窗体上特定组件类型的所有实例(其中一些位于面板或选项卡中,但全部在同一窗体上并由其拥有)。这是我现在拥有的:

for i := 0 to frmMain.ComponentCount - 1 do  
  begin  
    if frmMain.Components[i] is TMyClass then frmMain.Components[i].Destroy;  
  end;    

问题在于(我在编译之前就知道会出现这个问题)一旦我销毁了该组件,表单的组件列表将重新索引,导致数组越界。

有什么最好的方法来解决这个问题?我考虑过将“找到”的组件添加到一个单独的数组中,然后在此循环之后遍历该数组以删除它们,我认为这样会起作用...但是这是最好的方法吗?

先谢谢了


更新:

你们太棒了。谢谢:)

6个回答

28

你几乎是对的。你的循环应该看起来像:

for i := frmMain.ComponentCount - 1 downto 0 do
begin
  if frmMain.Components[i] is TMyClass then
    frmMain.Components[i].Free;
end;

这样一来,对函数“frmMain.ComponentCount”的调用将会在开始时完成而不会再次进行。

你也应该像上面那样调用Free,而不是Destroy——我现在记不清为什么要这样做了。 Bri


4
销毁是虚拟的。如果对象已经被销毁,那么销毁操作会失败。在调用销毁函数之前,必须进行空引用检查以确保对象是有效的。虽然在这里可能不会出现问题,但通常这是一个好习惯。 - Jim McKeeth
2
在这种情况下,直接调用Destroy是安全的。由于VCL管理此列表的方式,该列表中不太可能存在无效引用。即使有,Free也不能保护您,因为它依赖于实例为nil。 - Allen Bauer
2
请注意,循环从高到零进行,以确保考虑所有项目,否则循环可能会跳过已删除项目旁边的项目。重要的是不要错过这一点。 - mj2008
另一种获得相同效果的方法是将 frmMain.ComponentCount-1 放入一个变量中。 - Fabricio Araujo

10

从顶部开始,向后逆推。

即:

for i := frmMain.ComponentCount - 1 downto 0 do
begin
  if frmMain.Components[i] is TMyClass then frmMain.Components[i].Free;
end; 

使用Free来代替Destroy。在检查有效引用后,Free会调用Destroy。


1
他也应该调用Free而不是Destroy。一个人永远不应该调用Destroy,而总是应该调用Free。 - Nick Hodges

2

在你的情况下可能不会发生,但是if frmMain.Components[i] is TMyClass检查也将对TMyClass的派生类返回true。如果你真的想要删除一个特定的类,你可能需要添加一个额外的ClassName检查。


1

使用 while 循环的相同解决方案:

i := 0;
while i < frmMain.ComponentCount do
begin
  if frmMain.Components[i] is TMyClass then
    frmMain.Components[i].Free 
  else
    inc(i);
end;

这不会起作用 - (i) 正在增加,而 (frmMain.ComponentCount) 正在减少。 - Charles Faiga
1
如果你仔细看一下,你会发现当componentcount减少时我想要出现。如果每个组件都是TMyClass类的,那么i将保持为零,并且while循环将终止,因为0 < 0是false。 - Vegar

-2

如果需要在表单或面板中进行精细控制,可以使用以下代码

var

 i:Integer;

begin

for i := 0 to Panel1.ControlCount - 1 do

  begin

    if Panel1.Controls[i] is TEdit then
       Tedit(Panel1.Controls[i]).text := '';

  end;

-3
如果你需要检查和销毁一个已知的命名组件,请使用。
If YourComponent <> Nil Then
  YourComponent.Free;

2
这基本上与以下代码相同:if YourComponent <> nil then YourComponent.Destroy;双重判断是否有点不太合理呢? :-) - Uli Gerhardt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接