这是由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
是如何被捕获的以及何时被捕获的?
发布于 2011-03-01 21:10:42
当你有一个像问题中的函数,你有一个匿名方法访问局部变量时,Delphi似乎创建了一个TInterfacedObject后代,它捕获所有基于堆栈的变量作为它自己的公共变量。使用巴里的技巧来实现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;
当然,这段代码不能编译。我是没有魔术的:-)但这里的想法是在幕后创建一个“魔术”对象,从匿名方法引用的局部变量在魔术对象的公共字段中转换。该对象被用作接口(IUnkown),因此它会被引用计数。显然,同一个对象捕获了所有使用的变量并定义了所有匿名方法。
这应该同时回答“何时”和“如何”。
这是我用来调查的代码。在空白表单上放置一个TButton,这应该是整个单元。当您按下按钮时,您将按顺序在屏幕上看到以下内容:
TForm25.Button1Click$ActRec: TInterfacedObject
的方法实现的:这显示了实现背后的对象,它派生自TInterfacedObjectOnStack:string
:RTTI发现该对象上的该字段object.Self: TForm25
:RTTI发现该对象上的该字段。它用于获取ClasVar
FRefCount:Integer
的值-该值来自TInterfacedObjectClass 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.
https://stackoverflow.com/questions/5154914
复制相似问题