【问题标题】:problem iterating on an empty dictionary Delphi 10.4在空字典Delphi 10.4上迭代的问题
【发布时间】:2021-06-22 06:45:36
【问题描述】:

我有以下代码和两个字典。我可以看到,当我遍历 DictForbidden 时,即使它是空的,执行流程也会进入循环,所以 DictToAdd.remove(anInteger);执行与 anInteger 的旧值

var DictForbidden, DictToAdd : TDictionary<Integer,boolean>; 
var anInteger: Integer;
    
DictForbidden   := TDictionary<Integer,boolean>.Create;
DictToAdd       := TDictionary<Integer,boolean>.Create;
    
anInteger := 1;
DictToAdd.Add(anInteger,true);
    
for anInteger in DictForbidden.Keys do
  DictToAdd.remove(anInteger);
    
DictForbidden.Free;
DictToAdd.Free; 

我正在运行 Delphi 10.4,但我不记得在 10.3 中有这样的行为。我想当时如果字典为空,则没有输入循环。你知道这是否是 Delphi 10.4 上的新功能?或者我在这里做错了什么?

问候,卡洛斯

【问题讨论】:

  • 你没有做错任何事,这在我的 10.3 上按预期工作。目前无法访问 10.4。
  • 您确定确实调用了Remove 吗?如果将ShowMessage(DictToAdd.Count.ToString) 放在循环之后、清理之前会发生什么?
  • @AndreasRejbrand:在任何情况下它都可能报告 1,因为不能保证(即使它循环一次)它会删除值 1 - 它可能(如果编译器中存在错误) ) 在任意随机值处使用 anInteger 循环一次。最好在循环中包含一个“可见”语句,以查看循环的内部是否正在执行...
  • @HeartWare:正确,我的错。
  • 在列表中迭代时删除一个项目不幸的是给迭代器带来了一些麻烦。我建议使用反向 for 循环,以便使用 for-down-to 进行迭代或编写一个 while 循环。

标签: dictionary delphi iteration


【解决方案1】:

这对调试器来说有点烦人,因为循环当然会产生一些可执行代码——尤其是一个 for in 循环,它调用了 MoveNextCurrent 以及它之后的枚举器的一些清理代码完成。

此代码必须位于某个地方,并且该代码的调试器使用的行信息(也称为“蓝点”)通常与您在本例中编写的实际代码重叠。

为了说明这一点,我将您的代码放入控制台应用程序时,反汇编的样子。

正如你所看到的,这里显示了两个断点,它们属于我在第 21 行放置的同一个断点。但被击中的是一个带有清理代码的断点,它负责销毁枚举器。如果您按 F7 并使用 debug dcus 进行编译,您会注意到它实际上并没有进入 TDictionary.Remove 而是进入 TObject.Destroy

现在是放置beginend后的反汇编:

再次显示了两个断点,但这次我实际上是自己放置了它们 - 它们用于不同的行。

【讨论】:

    【解决方案2】:

    非常感谢您的快速帮助!

    其实你是对的@Heartware。在循环之后,DictToAdd 仍然包含一个 1。所以这里基本上没有 case 而是在 DictToAdd.remove(anInteger); 行中放置一个断点。执行停止,然后将光标放在 anInteger 上方,您会看到该值为 1 但未删除 1 的步进。当用 showmessage 替换删除时,我观察到断点到达但未执行 showmessage 的同一件事。

    还注意到当使用这样的开始/结束时

    for anInteger in DictForbidden.Keys do
    begin
      DictToAdd.remove(anInteger);
    end;
    

    没有到达断点。

    【讨论】:

    • 记得接受回答您问题的答案和/或支持您认为有用的答案。
    【解决方案3】:

    在我的 10.4.1 中它没有进入循环。但是由于编译器进行了优化,当您调试跟踪时,它可能看起来像这样。尝试更换

    DictToAdd.Remove
    

    有一个

    writeln(anInteger) // if Console Application
    ShowMessage(IntToStr(anInteger)) // if GUI Application
    

    看看有没有报告。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-24
      • 2021-02-20
      • 1970-01-01
      • 2020-07-14
      • 2019-05-29
      • 2012-10-15
      • 1970-01-01
      • 2011-03-17
      相关资源
      最近更新 更多