Anonymous Methods and Closures

Anonymous methods are reference counted interfaces under the hood. Captured variables are shared by reference.

Used heavily with TThread.Queue/Synchronize, TTask.Run, TParallel.For, sorting comparers, REST async callbacks, and timers.

Pitfall: capturing loop variables. All closures see the final value. Capture through a helper function to get a per-iteration copy.

type
  TIntFunc = reference to function(X: Integer): Integer;
  TProc = reference to procedure;            // TProc, TProc<T>, TFunc<TResult>, TFunc<T, TResult>, TPredicate<T> exist in System.SysUtils

function MakeMultiplier(Factor: Integer): TIntFunc;
begin
  Result := function(X: Integer): Integer
            begin
              Result := X * Factor;          // captures Factor (closure)
            end;
end;

function MakeCounter: TFunc<Integer>;
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 Items: TArray<string>; const Action: TProc<string>);
begin
  for var Item in Items do Action(Item);
end;

var Triple := MakeMultiplier(3);
Writeln(Triple(10));                         // 30
var Counter := MakeCounter;
Writeln(Counter, ' ', Counter);              // 1 2
ForEach(['a', 'b'], procedure(S: string) begin Writeln(S.ToUpper); end);

var IsEven: TPredicate<Integer> := function(N: Integer): Boolean begin Result := not Odd(N); end;