【问题标题】:Delphi reference to procedure weirdness德尔福对程序怪异的参考
【发布时间】:2020-09-10 19:21:53
【问题描述】:

在 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(),还适用于任何可以接受参数和返回值的函数,例如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

【问题讨论】:

  • 奇怪。在 XE2 中,这根本不编译。无论如何,你可能希望“MethodOfRecord”成为一个类过程,然后你会像“proc := TRecord.MethodOfRecord”一样分配。

标签: delphi delphi-10.3-rio


【解决方案1】:
proc := MakeRecord().MethodOfRecord;

这里proc是一个匿名方法,但MethodOfRecord不是,它是一个正常的过程。您可能预计会出现编译错误,但编译器会在后台为您完成一些工作。它将您的代码变成这样:

proc := 
  procedure 
  begin 
    MakeRecord().MethodOfRecord; 
  end;

现在右边是一个匿名方法,你可以看到你的程序为什么会这样。

如果您不希望在每次调用时都创建新记录,则需要为要使用的记录实例声明一个局部变量。

【讨论】:

  • 这很可能是发生了什么,但这是记录在案的行为吗?没有here 提到这样的结构。
  • @Olivier 这就是正在发生的事情。我不知道文档,是否有任何官方存在。
  • @DavidHeffernan 我想这种“包装”不仅适用于reference to procedure(),还适用于任何无参数函数?
  • 甚至不需要无参数
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多