gpt4 book ai didi

delphi - Delphi 的匿名方法中引用的变量如何以及何时被捕获?

转载 作者:行者123 更新时间:2023-12-03 14:34:10 26 4
gpt4 key购买 nike

这是由 How to compare TFunc/TProc containing function/procedure of object? 提示的,特别是大卫对巴里问题的评论。由于我没有博客可以发布此问题,因此我将在这里提出这个问题并回答它。

问题:何时以及如何捕获 Delphi 匿名方法中引用的变量?

示例:

procedure ProcedureThatUsesAnonymousMethods;
var V: string;
F1: TFunc<string>;
F2: TFunc<string>;
begin
F1 := function: string
begin
Result := V; // references local variable
end
V := '1';
F2 := function: string
begin
Result := V;
end
V := '2';
ShowMessage(F1);
ShowMessage(F2);
end;

两个 ShowMessage 都将显示 2。为什么? V 如何以及何时被捕获?

最佳答案

当您有一个像问题中的函数一样的函数时,您有一个访问局部变量的匿名方法,Delphi 似乎会创建一个 TInterfacedObject 后代,它捕获所有基于堆栈的变量作为它自己的公共(public)变量。使用 Barry 的技巧来实现 TObject 和一些 RTTI,我们可以看到整个事情的实际情况。

实现背后的神奇代码可能如下所示:

// Magic object that holds what would normally be Stack variables and implements
// anonymous methods.
type ProcedureThatUsesAnonymousMethods$ActRec = class(TInterfacedObject)
public
V: string;
function AnonMethodImp: string;
end;

// The procedure with all the magic brought to light
procedure ProcedureThatUsesAnonymousMethods;
var MagicInterface: IUnknown;
F1: TFunc<string>;
F2: TFunc<string>;
begin
MagicInterface := ProcedureThatUsesAnonymousMethods$ActRec.Create;
try
F1 := MagicInterface.AnonMethod;
MagicInterface.V := '1';
F2 := MagicInterface.SomeOtherAnonMethod;
MagicInterface.V := '2';
ShowMessage(F1);
ShowMessage(F2);
finally MagicInterface := nil;
end;
end;

当然,这段代码无法编译。我没有魔法:-)但这里的想法是在幕后创建一个“魔法”对象,并且从匿名方法引用的局部变量在魔法对象的公共(public)字段中进行转换。该对象用作接口(interface)(IUnkown),因此它会被引用计数。显然,同一个对象捕获所有使用的变量并定义所有匿名方法。

这应该回答“何时”和“如何”。

这是我用来调查的代码。将 TButton 放在空白表单上,这应该是整个单元。当您按下按钮时,您将在屏幕上按顺序看到以下内容:

  • 000000(虚假号码)
  • 000000(相同的数字):这证明两个匿名方法实际上都是作为同一对象的方法实现的!
  • TForm25.Button1Click$ActRec: TInterfacedObject :这显示了实现背后的对象,它派生自 TInterfacedObject
  • OnStack:string:RTTI 在该对象上发现此字段。
  • Self:TForm25:RTTI 在该对象上发现此字段。用于获取ClasVar
  • 的值
  • FRefCount:Integer - 这来自 TInterfacedObject
  • Class Var - ShowMessage 的结果。
  • On Stack - ShowMessage 的结果。

代码如下:

unit Unit25;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Rtti;

type
TForm25 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
ClassVar: string;
public
end;

var
Form25: TForm25;

implementation

{$R *.dfm}

procedure TForm25.Button1Click(Sender: TObject);
var F1: TFunc<string>;
F2: TFunc<string>;

OnStack: string;

i: IInterface;
o: TObject;

RC: TRttiContext;
R: TRttiType;
RF: TRttiField;

begin
// This anonymous method references a member field of the TForm class
F1 := function :string
begin
Result := ClassVar;
end;

i := PUnknown(@F1)^;
o := i as TObject;
ShowMessage(IntToStr(Integer(o))); // I'm looking at the pointer to see if it's the same instance as the one for the other Anonymous method

// This anonymous method references a stack variable
F2 := function :string
begin
Result := OnStack;
end;

i := PUnknown(@F2)^;
o := i as TObject;
ShowMessage(IntToStr(Integer(o)));

ShowMessage(o.ClassName + ': ' + o.ClassType.ClassParent.ClassName);

RC.Create;
try
R := RC.GetType(o.ClassType);
for RF in R.GetFields do
ShowMessage(RF.Name + ':' + RF.FieldType.Name);
finally RC.Free;
end;

ClassVar := 'Class Var';
OnStack := 'On Stack';

ShowMessage(F1);
ShowMessage(F2);
end;

end.

关于delphi - Delphi 的匿名方法中引用的变量如何以及何时被捕获?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5154914/

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