首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何以及何时捕获Delphi匿名方法中引用的变量?

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

Stack Overflow用户
提问于 2011-03-01 21:10:21
回答 1查看 2K关注 0票数 22

这是由How to compare TFunc/TProc containing function/procedure of object?引起的,特别是大卫对巴里问题的评论。因为我没有博客可以发表这篇文章,所以我将在这里问这个问题,并回答它。

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

示例:

代码语言:javascript
复制
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是如何被捕获的以及何时被捕获的?

EN

回答 1

Stack Overflow用户

发布于 2011-03-01 21:10:42

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

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

代码语言:javascript
复制
// 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(相同的数字):这证明了两个匿名方法实际上都是作为同一个object!
  • TForm25.Button1Click$ActRec: TInterfacedObject的方法实现的:这显示了实现背后的对象,它派生自TInterfacedObject
  • OnStack:string:RTTI发现该对象上的该字段object.
  • Self: TForm25:RTTI发现该对象上的该字段。它用于获取ClasVar
  • FRefCount:Integer的值-该值来自TInterfacedObject
  • Class Var - ShowMessage.
  • On Stack的结果- ShowMessage.

的结果

代码如下:

代码语言:javascript
复制
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.
票数 27
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5154914

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档