In Delphi, you can apparently assign an entire chain of method calls to a single variable:
program What;
{$APPTYPE CONSOLE}
type
TProc = reference to procedure();
TRecord = record
procedure MethodOfRecord();
end;
procedure TRecord.MethodOfRecord();
begin
WriteLn('MethodOfRecord finished');
end;
function MakeRecord(): TRecord;
begin
WriteLn(' MakeRecord finished');
end;
var
proc: TProc;
begin
proc := MakeRecord().MethodOfRecord;
proc();
proc();
proc();
end.
In this code, I create an anonymous TRecord
, assign its method TRecord.MethodOfRecord
to a reference to procedure
, and then call it 3 times.
Question: How many times will MakeRecord
be called?
Answer: 3 times:
MakeRecord finished
MethodOfRecord finished
MakeRecord finished
MethodOfRecord finished
MakeRecord finished
MethodOfRecord finished
Every time I call proc
, it calls MakeRecord
first. This seems weird. Why does this happen? I expected it to be called only once. Is it because it's anonymous? Of course if I give the TRecord a name, it gets called only once:
var
proc: TProc;
rec: TRecord;
begin
rec := MakeRecord();
proc := rec.MethodOfRecord;
proc();
proc();
proc();
end.
this outputs:
MakeRecord finished
MethodOfRecord finished
MethodOfRecord finished
MethodOfRecord finished
Can someone explain the logic behind this behavior? Is this documented somewhere?
This is Embarcadero® Delphi 10.3.
Update:
This works not only with reference to procedure()
, but also with reference to any function that can take arguments and return values, for example with TProc = reference to function(s: string): string;
.
program What;
{$APPTYPE CONSOLE}
uses System.SysUtils;
type
TProc = reference to function(s: string): string;
TRecord = record
FirstHalf: string;
function Combine(secondHalf: string): string;
end;
function TRecord.Combine(secondHalf: string): string;
begin
Result := Format('%s + %s', [FirstHalf, secondHalf]);
WriteLn(Format(' Combine finished with secondHalf = %s', [secondHalf]));
end;
function MakeRecord(firstHalf: string): TRecord;
begin
Result.FirstHalf := firstHalf;
WriteLn(Format('MakeRecord finished with firstHalf = %s', [firstHalf]));
end;
var
proc: TProc;
msg: string;
begin
proc := MakeRecord(msg).Combine;
msg := 'A';
WriteLn(proc('a'));
msg := 'B';
WriteLn(proc('b'));
msg := 'C';
WriteLn(proc('c'));
end.
This outputs:
MakeRecord finished with firstHalf = A
Combine finished with secondHalf = a
A + a
MakeRecord finished with firstHalf = B
Combine finished with secondHalf = b
B + b
MakeRecord finished with firstHalf = C
Combine finished with secondHalf = c
C + c