Anonymous Methods & Closures
An anonymous method is a routine without a name, written inline, that can capture variables from its surrounding scope (a closure).
30.1 Method references
An anonymous method is a routine without a name, written inline, that can capture variables from its surrounding scope (a closure). Supported by Delphi 2009+; FPC supports them in the development version (3.3.1/trunk) with {$modeswitch functionreferences} and {$modeswitch anonymousfunctions} — not in FPC 3.2.x.
program Closures;
{$IFDEF FPC}
{$mode delphi}
{$modeswitch functionreferences}
{$modeswitch anonymousfunctions}
{$ENDIF}
{$APPTYPE CONSOLE}
uses SysUtils;
type
TIntFunc = reference to function(X: Integer): Integer;
TCounterFunc = reference to function: Integer;
function MakeMultiplier(Factor: Integer): TIntFunc;
begin
Result := function(X: Integer): Integer
begin
Result := X * Factor; // Factor is captured
end;
end;
function MakeCounter: TCounterFunc;
var
Count: Integer;
begin
Count := 0;
Result := function: Integer
begin
Inc(Count); // captured variable lives on after MakeCounter returns
Result := Count;
end;
end;
procedure ForEach(const A: array of Integer; F: TIntFunc);
var
X: Integer;
begin
for X in A do
Write(F(X), ' ');
Writeln;
end;
var
Triple: TIntFunc;
Next: TCounterFunc;
begin
Triple := MakeMultiplier(3);
ForEach([1, 2, 3], Triple); // 3 6 9
ForEach([1, 2, 3], function(X: Integer): Integer
begin
Result := X * X;
end); // 1 4
…30.2 Predefined reference types
Delphi System.SysUtils declares generic method references:
reference to procedure TProc<T>... TProc<T1..T4> procedure with 1–4 parameters
TFunc<TResult>... TFunc<T1..T4, TResult> function with 0–4 parameters
reference to function(Arg1: T): Boolean
30.3 How capture works
Captured variables are moved into a hidden, reference-counted heap object shared by the enclosing routine and all closures that capture them — capture is by reference, not by value.
Consequence: closures created in a loop all see the final value of the loop variable. Create each closure in a separate function to capture a copy:
A reference to variable can also hold ordinary routines and methods.
Anonymous methods are interfaces under the hood: they are reference-counted and freed automatically.
function MakePrinter(N: Integer): TProc;
begin
Result := procedure begin Writeln(N); end; // N is this call's own copy
end;30.4 Typical uses
Callbacks and event handlers created at run time.
Sorting comparers (TComparer<T>.Construct).
Background work: TThread.CreateAnonymousThread(procedure begin ... end).Start;, TThread.Queue(nil, procedure begin ... end);, TTask.Run(...).
Deferred execution, retry helpers, and simple dependency injection.
30.5 Alternatives in FPC 3.2
Method pointers (procedure of object) with a small helper class holding the state.
Nested procedural variables ({$modeswitch nestedprocvars}, type procedure(X: Integer) is nested) — can access the enclosing routine's locals while it is still running (no capture beyond its lifetime).