gpt4 book ai didi

delphi - 在 pascal 中动态分配匿名函数

转载 作者:行者123 更新时间:2023-12-03 14:48:07 24 4
gpt4 key购买 nike

我希望能够在 pascal 中动态生成弹出菜单。

我还希望能够为每个菜单项动态分配 OnClick 处理程序。

这是我习惯在 C# 中做的事情,这是我在 pascal 中的尝试。

菜单项 onClick 事件处理程序需要属于一个对象(of Object),因此我为此创建了一个容器对象。

这是我的代码:

unit Unit1;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Menus;

type
TForm1 = class(TForm)
PopupMenu1: TPopupMenu;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

TFoo = class
public
Bar : String;
Val : Integer;
end;

TNotifyEventWrapper = class
private
FProc: TProc<TObject>;
I : Integer;
public
constructor Create(Proc: TProc<TObject>);
published
procedure Event(Sender: TObject);
end;

var
Form1: TForm1;
NE : TNotifyEventWrapper;

implementation

{$R *.dfm}

constructor TNotifyEventWrapper.Create(Proc: TProc<TObject>);
begin
inherited Create;
FProc := Proc;
end;

procedure TNotifyEventWrapper.Event(Sender: TObject);
begin
ShowMessage(IntToStr(I));
FProc(Sender);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
F : TFoo;
I: Integer;
mi : TMenuItem;
begin
if Assigned(NE) then FreeAndNil(NE);

for I := 1 to 10 do
begin
F := TFoo.Create;
F.Bar := 'Hello World!';
F.Val := I;
NE := TNotifyEventWrapper.Create
(
procedure (Sender :TObject)
begin
ShowMessage(F.Bar + ' ' + inttostr(F.Val) + Format(' Addr = %p', [Pointer(F)]) + Format('Sender = %p, MI.OnClick = %p', [Pointer(Sender), Pointer(@TMenuItem(Sender).OnClick)]));
end
);
NE.I := I;

mi := TMenuItem.Create(PopupMenu1);

mi.OnClick := NE.Event;

mi.Caption := inttostr(F.Val);

PopupMenu1.Items.Add(mi);
end;
end;

end.

MenuItems

单击菜单项编号 6

程序显示预期消息

Menu6

但是下一条消息没有显示预期结果。

显示项目 10,而不是 6

Menu10

无论我点击列表中的哪一项,它们似乎都会触发列表中最后一项的事件处理程序 (10)。

有人建议我,NE 对象的成员过程 Event 对于该对象的所有实例来说是相同的内存地址。

无论我点击哪个菜单项,内存地址MI.OnClick都是相同的。

最佳答案

理解这一点的关键是要理解变量捕获捕获变量而不是

您的匿名方法都捕获相同的变量F。由于 FormCreate 仅执行一次,因此该变量只有一个实例。这解释了这种行为。当您的匿名方法执行时,变量 F 会在最终循环迭代中为其分配值。

您需要的是每个不同的匿名方法捕获不同的变量。您可以通过在生成每个不同的匿名方法时创建一个新的堆栈帧来做到这一点。

function GetWrapper(F: Foo): TNotifyEventWrapper;
begin
Result := TNotifyEventWrapper.Create(
procedure(Sender: TObject)
begin
ShowMessage(F.Bar + ...);
end
);
end;

由于函数 GetWrapper 的参数是该函数堆栈帧中的局部变量,因此每次调用 GetWrapper 都会创建该局部变量的一个新实例。

您可以将 GetWrapper 放置在您喜欢的位置。作为 FormCreate 中的嵌套函数,或作为私有(private)方法,或在单元范围内。

然后像这样构建你的菜单:

F := TFoo.Create;
F.Bar := 'Hello World!';
F.Val := I;
NE := GetWrapper(F);
NE.I := I;

相关阅读:

关于delphi - 在 pascal 中动态分配匿名函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29159763/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com