delphi - 是否可以捕获 Delphi 匿名方法/闭包的本地变量值?

标签 delphi closures anonymous-methods

Delphi 实现匿名方法的有趣方面之一
/closures 是捕获例程本地变量状态的能力
从中调用 anon 方法。这在 Marco Cantu 的一本书(Delphi 2009?)中有很好的描述。

我的问题是,有没有办法类似地捕获变量的值
哪些是匿名方法本身的本地?

为了希望说明我的意思,请考虑以下代码片段,
这不符合我的意思:

type

  TMemoProc = reference to procedure(Memo: TMemo; StepNo : Integer);

  TForm1 = class(TForm)
  [...]
  private
    procedure Process(Memo: TMemo; StepNo: Integer);
  public
    { Public declarations }
    MemoProc : TMemoProc;
    procedure CreateMemoProc;
  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  for i := 1 to 3 do
    MemoProc(Memo1, i);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  CreateMemoProc;
  MemoProc(Memo1, 0);
end;

procedure TForm1.Process(Memo : TMemo; StepNo : Integer);
var
  Count : Integer;
begin
  if StepNo = 0 then
    Count := 1
  else
    Inc(Count);
  Memo.Lines.Add(Self.Name + ' ' +Format('stepno: %d, count: %d', [StepNo, Count]));
end;

procedure TForm1.CreateMemoProc;
begin
  MemoProc :=
    procedure (Memo : TMemo; StepNo : Integer)
    var
      Count : Integer;
    begin
      if StepNo = 0 then
        Count := 1
      else
        Inc(Count);
      Memo.Lines.Add(Format('stepno: %d, count: %d', [StepNo, Count]));
    end;
end;

点击 Button1 几次,插入的数字就不足为奇了
改变,正如你所期望的那样,Delphi 没有隐式初始化
堆栈上的简单变量。

但我想知道是否有人能想出一个巧妙的方法来
在调用它之间捕获 anon 方法的局部变量。

Fwiw,我想知道的原因是有一些用例
通常需要两次调用 anon 方法,一次执行某些操作
初始化和一个做一些迭代,最简洁的编码方式
如果可以以某种方式捕获 anon 方法的局部变量。

我想到的那种用例是编写一些通用代码来迭代数据集,在其中指定一个 bool 函数会很方便,该函数通过引用行的字段值来确定是否应该处理特定的数据集行以及另一个它定义了当 bool 函数返回 true 时应该对行执行的操作。

在传统实现中,可以将字段指定为 DataSet.FieldByName('xxx'),但是在涉及许多字段和/或许多行的情况下,这可能非常低效。

所以我的q背后的议程基本上是能够指定anon方法的初始化调用中涉及的字段,然后直接使用它们(如DataSet11FieldCatflap,而不是DataSet.FieldByName('Catflap')。我思考过使用容器化的字段列表在初始化调用和迭代之间进行传递,但这对我来说似乎“不满意”。

最佳答案

匿名方法从其封闭范围捕获局部变量的状态。如果方法的实现需要更多状态,则在封闭范围内声明更多局部变量,即使它们只在匿名方法的范围内使用过。

在您的情况下,这意味着声明和初始化 CountCreateMemoProc方法而不是存储在 MemoProc 中的匿名方法内部.

一般来说,将变量声明在错误的范围内可能会让人难看和困惑。为避免这种情况,您可以使用方法工厂,其唯一工作是生成匿名方法实例。然后,关于变量声明位置的任何奇怪之处都仅限于该一个函数,因此目的很明确。你在 CreateMemoProc 中几乎都有。已经,但如果由我决定,它将是一个独立的函数,而不是以下形式的方法:

function CreateMemoProc: TMemoProc;
var
  Count: Integer;
begin
  Count := 0;
  Result := procedure (Memo : TMemo; StepNo : Integer)
    begin
      if StepNo = 0 then
        Count := 1
      else
        Inc(Count);
      Memo.Lines.Add(Format('stepno: %d, count: %d', [StepNo, Count]));
    end;
end;
Count变量是 CreateMemoProc 的本地变量,并且在匿名方法中使用,匿名方法捕获它。每次调用 CreateMemoProc创建 Count 的新副本,所以每个匿名方法都知道它被调用了多少次。

但我得到的印象是您并不想包含 StepNo方法中的参数。相反,您希望该方法“知道”它是否已经被调用。在这种情况下,我们可以增加 Count带有标志:
type
  TMemoProc = reference to procedure(Memo: TMemo);

function CreateMemoProc: TMemoProc;
var
  Count: Integer;
  Initialized: Boolean;
begin
  Count := 0;
  Initialized := False;
  Result := procedure(Memo: TMemo)
    begin
      if not Initialized then begin
        Count := 0;
        Initialized := True;
      end;
      Inc(Count);
      Memo.Lines.Add(Format('count: %d', [Count]));
    end;
end;

关于delphi - 是否可以捕获 Delphi 匿名方法/闭包的本地变量值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31193949/

相关文章:

java - 太阳java6关闭

c# - 使用 Func<> 参数将委托(delegate)对象传递给方法

delphi - firemonkey - 如何以小写字母启动移动键盘?

json - Delphi 11.3 - 无法从变体创建/添加 JSON

事件监听器中的 JavaScript 闭包

c# - 事件处理程序和匿名委托(delegate)/Lambda 表达式

c# - 在设计应用程序时如何使用 Func<> 和 Action<>?

windows - JumpList 到 Delphi 上所有打开的表单

delphi - TFlowPanel。在运行时添加控件

class - Swift 类省略带闭包的括号 : syntactic sugar or something else?