Quantcast
Viewing all articles
Browse latest Browse all 10

Beware using anonymous methods in loops

Quick, what’s the output of this simple routine?

procedure AnonLoop;
var
  i: integer;
  proc: TProc;
  ProcList: TList<TProc>;
begin
  ProcList := TList<TProc>.Create;
  for i := 1 to 5 do
    ProcList.Add(
      procedure()
      begin
        write(i, ' ');
      end);

  for proc in ProcList do
    proc;
  procList.Free;
end;

Intuitively, you’d think it would write “1 2 3 4 5″, but you’d be wrong.  It actually prints out “6 6 6 6 6″.  Huh?  6s?  You never fed it a 6!  And why are they all the same?  Well, it has to do with the way anonymous methods work.  Take a look at my analysis from a while back and see if it becomes any clearer.

Basically, you aren’t creating a new anonymous method each time you go through the loop.  The functor object is created one time only, on the begin line of the enclosing method, not once each time you hit the declaration in the code.

This is a bit disorienting, but technically necessary; if there could be more than one instance of the functor object created each time you run the enclosing method, it would be impossible to support multiple anonymous methods enclosing the same local variables, among other gotchas.

So what to do about it?  Seems to me that we have two problems.  First, you can’t create multiple anonymous methods by putting a declaration in a loop.  Second, the compiler hides this issue from you by allowing the anonymous method to capture the index variable of a for loop, which almost certainly results in silently generating bad code.

To create multiple anonymous methods in a loop, you need to call another function that returns an anonymous method.  This can make things a bit difficult, especially since it negates (or at least lessens) the ability of the anonymous method to capture local variables, which is what makes it so useful.

The compiler already can’t capture Result and var/out parameters, because they don’t belong to the current procedure.  Seems to me that the next version of the Delphi compiler should similarly check for this, and emit an error if an anonymous method tries to capture the index variable of a for loop, because that almost certainly doesn’t belong to the anonymous method.

BTW I know Barry Kelly reads at least some of my posts, because he occasionally responds with comments here when I write about technical issues. Barry, if you see this, would you mind weighing in on the problem? Thanks.


Viewing all articles
Browse latest Browse all 10

Trending Articles