problem iterating on an empty dictionary Delphi 10.4

Viewed 159

I have the following code with two dictionaries. I can see that when I iterate through DictForbidden even though it is empty the execution flow enters the loop so DictToAdd.remove(anInteger); is executed with the old value of 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; 

I am running Delphi 10.4 and I don't recall such a behavior in 10.3. I think back then if the dictionary was empty the loop was not entered. Do you know if this is something new on Delphi 10.4?, or maybe I am doing something wrong here?

regards, Carlos

3 Answers

In my 10.4.1 it doesn't enter the loop. But it may look like it does when you debug trace due to optimization made by the compiler. Try replacing the

DictToAdd.Remove

with a

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

and see if anything is reported.

thank you very much for the quick help!

Actually you are right @Heartware. After the loop, DictToAdd still contains a 1. So basically there is no case here rather than putting a break point in the line DictToAdd.remove(anInteger); the execution is stopped and putting then the cursor above anInteger you see the value is 1 but stepping over the 1 is not being removed. When replacing the removal by a showmessage I observe the same thing the break point is reached but the showmessage is not executed.

Also noticed that when using a begin/end like this

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

the break point is not reached.

This is a little annoyance with the debugger caused by the fact that a loop of course produces some executable code - especially a for in loop that has calls to MoveNext and Current and some cleanup code for the enumerator after it completes.

This code has to go somewhere and often the line information being used by the debugger (aka "the blue dots") of that code overlap with actual code you wrote like in this case.

To illustrate this here is how the disassembly looks like when I put your code into a console application.

enter image description here

As you can see there are two breakpoints displayed here which belong to the same one breakpoint I placed in line 21. But the one that got hit is the one with the cleanup code which is responsible for destroying the enumerator. If you hit F7 and you compiled with debug dcus you will notice that it actually does not go into TDictionary.Remove but into TObject.Destroy.

Now the disassembly after placing the begin and end:

enter image description here

Again two breakpoints shown but this time I actually placed them both myself - and they are for different lines.

Related