在Delphi的匿名方法中引用的变量和何时被捕获?

前端之家收集整理的这篇文章主要介绍了在Delphi的匿名方法中引用的变量和何时被捕获?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
这是由 How to compare TFunc/TProc containing function/procedure of object?提出的,特别是David对Barry的问题的评论。因为我没有一个博客来发布这个,我将在这里提出这个问题,并回答。

问题: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后代,它捕获所有基于堆栈的变量,因为它是自己的公共变量。使用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;

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

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

这是我以前调查的代码。把一个TButton放在空白的表单上,这应该是整个单位。当您按下按钮时,您将在屏幕上看到以下内容

> 000000(虚假号码)
> 000000(相同数字):这两个匿名方法的证明实际上是作为同一个对象的方法实现的!
> TForm25.Button1Click $ ActRec:TInterfacedObject:这显示了实现后面的对象,它来自于TInterfacedObject
> OnStack:string:RTTI发现该对象上的该字段。
> Self:TForm25:RTTI发现该对象上的该字段。它用于获取ClasVar的值
> FRefCount:Integer – 这来自TInterfacedObject
> Class Var – ShowMessage的结果。
>堆栈 – 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相关文章