Delphi 提及程序异常
Delphi reference to procedure weirdness
在 Delphi 中,您显然可以将整个方法调用链分配给单个变量:
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.
在这段代码中,我创建了一个匿名的 TRecord
,将其方法 TRecord.MethodOfRecord
分配给 reference to procedure
,然后调用它 3 次。
问题:MakeRecord
会被调用多少次?
答案:3次:
MakeRecord finished
MethodOfRecord finished
MakeRecord finished
MethodOfRecord finished
MakeRecord finished
MethodOfRecord finished
每次调用proc
,它都会先调用MakeRecord
。这看起来很奇怪。为什么会这样?我希望它只被调用一次。是因为它是匿名的吗?当然,如果我给 TRecord 一个名字,它只会被调用一次:
var
proc: TProc;
rec: TRecord;
begin
rec := MakeRecord();
proc := rec.MethodOfRecord;
proc();
proc();
proc();
end.
这输出:
MakeRecord finished
MethodOfRecord finished
MethodOfRecord finished
MethodOfRecord finished
谁能解释一下这种行为背后的逻辑?这在某处记录了吗?
这是 Embarcadero® Delphi 10.3.
更新:
这不仅适用于 reference to procedure()
,而且适用于任何可以接受参数和 return 值的函数,例如 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.
这输出:
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;
这里proc
是匿名方法,MethodOfRecord
不是,是正常的程序。您可能预料到会出现编译错误,但编译器会在后台为您做一些工作。它将您的代码变成这样:
proc :=
procedure
begin
MakeRecord().MethodOfRecord;
end;
现在右边是一个匿名方法,你可以看到为什么你的程序会这样运行。
如果您不想在每次调用时都创建新记录,则需要为要使用的记录实例声明一个局部变量。
在 Delphi 中,您显然可以将整个方法调用链分配给单个变量:
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.
在这段代码中,我创建了一个匿名的 TRecord
,将其方法 TRecord.MethodOfRecord
分配给 reference to procedure
,然后调用它 3 次。
问题:MakeRecord
会被调用多少次?
答案:3次:
MakeRecord finished
MethodOfRecord finished
MakeRecord finished
MethodOfRecord finished
MakeRecord finished
MethodOfRecord finished
每次调用proc
,它都会先调用MakeRecord
。这看起来很奇怪。为什么会这样?我希望它只被调用一次。是因为它是匿名的吗?当然,如果我给 TRecord 一个名字,它只会被调用一次:
var
proc: TProc;
rec: TRecord;
begin
rec := MakeRecord();
proc := rec.MethodOfRecord;
proc();
proc();
proc();
end.
这输出:
MakeRecord finished
MethodOfRecord finished
MethodOfRecord finished
MethodOfRecord finished
谁能解释一下这种行为背后的逻辑?这在某处记录了吗?
这是 Embarcadero® Delphi 10.3.
更新:
这不仅适用于 reference to procedure()
,而且适用于任何可以接受参数和 return 值的函数,例如 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.
这输出:
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;
这里proc
是匿名方法,MethodOfRecord
不是,是正常的程序。您可能预料到会出现编译错误,但编译器会在后台为您做一些工作。它将您的代码变成这样:
proc :=
procedure
begin
MakeRecord().MethodOfRecord;
end;
现在右边是一个匿名方法,你可以看到为什么你的程序会这样运行。
如果您不想在每次调用时都创建新记录,则需要为要使用的记录实例声明一个局部变量。