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
proc := MakeRecord().MethodOfRecord;
Here proc
is an anonymous method, but MethodOfRecord
is not, it's a normal procedure. You might anticipate a compilation error, but the compiler does a little work in the background for you. It turns your code into this:
proc :=
procedure
begin
MakeRecord().MethodOfRecord;
end;
Now the right hand side is an anonymous method, and you can see why your program behaves as it does.
If you don't want a new record to be created on each invocation, you'd need to declare a local variable for the record instance that you want to be used.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With