简体   繁体   English

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

[英]How and when are variables referenced in Delphi's anonymous methods captured?

This was prompted by How to compare TFunc/TProc containing function/procedure of object? 这是由如何比较TFunc / TProc包含对象的功能/过程? , specifically by David's comment to Barry's question. ,特别是大卫对巴里问题的评论。 Since I don't have a Blog to post this to I'm going to ask this question here, and answer it. 由于我没有博客发布这个问题,我将在这里提出这个问题并回答。

Question : When and how are variables referenced in Delphi's anonymous methods captured? 问题 :Delphi的匿名方法中何时以及如何引用变量?

Example: 例:

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;

Both ShowMessage are going to show 2 . 两个ShowMessage都将显示2 Why? 为什么? How does V get captured and when? 如何捕获V以及何时捕获?

When you have a function like the one in the question, where you have an anonymous method accessing a local variable, Delphi appears to create one TInterfacedObject descendant that captures all the stack based variables as it's own public variables. 当你有一个类似问题的函数,你有一个访问局部变量的匿名方法时,Delphi似乎创建了一个TInterfacedObject后代,它捕获所有基于堆栈的变量,因为它是自己的公共变量。 Using Barry's trick to get to the implementing TObject and a bit of RTTI we can see this whole thing in action. 使用Barry的技巧来实现TObject和一些RTTI,我们可以看到整个事情的实际应用。

The magic code behind the implementation probably looks like this: 实现背后的神奇代码可能如下所示:

// 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;

Of course this code doesn't compile. 当然这段代码不能编译。 I'm magic-less :-) But the idea here is that an "Magic" object is created behind the scenes and local variables that are referenced from the anonymous method are transformed in public fields of the magic object. 我很神奇:-)但是这里的想法是在幕后创建一个“魔术”对象,从匿名方法引用的局部变量在魔术对象的公共字段中转换。 That object is uses as an interface (IUnkown) so it gets reference-counted. 该对象用作接口(IUnkown),因此它被引用计数。 Apparently the same object captures all used variables AND defines all the anonymous methods. 显然,同一个对象捕获所有使用的变量并定义所有匿名方法。

This should answer both "When" and "How". 这应该回答“何时”和“如何”。

Here's the code I used to investigate. 这是我用来调查的代码。 Put a TButton on a blank form, this should be the whole unit. 把TButton放在一个空白表格上,这应该是整个单位。 When you press the button you'll see the following on screen, in sequence: 当您按下按钮时,您将在屏幕上按顺序看到以下内容:

  • 000000 (bogus number) 000000(假号)
  • 000000 (the same number): This proofs both anonymous methods are actually implemented as methods of the same object! 000000(相同的数字):这证明两个匿名方法实际上都是作为同一个对象的方法实现的!
  • TForm25.Button1Click$ActRec: TInterfacedObject : This shows the object behind the implementation, it's derived from TInterfacedObject TForm25.Button1Click$ActRec: TInterfacedObject :这显示了实现背后的对象,它来自TInterfacedObject
  • OnStack:string : RTTI discovers this field on that object. OnStack:string :RTTI在该对象上发现此字段。
  • Self: TForm25 : RTTI discovers this field on that object. Self: TForm25 :RTTI在该对象上发现此字段。 It's used to get the value of ClasVar 它用于获取ClasVar的值
  • FRefCount:Integer - this comes from TInterfacedObject FRefCount:Integer - 来自TInterfacedObject
  • Class Var - result of ShowMessage. Class Var - ShowMessage的结果。
  • On Stack - result of ShowMessage. On Stack - ShowMessage的结果。

Here's the code: 这是代码:

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.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Delphi匿名方法 - 赞成和利弊。 在Delphi中使用闭包(anonymus方法)时的良好实践 - Delphi anonymous methods - pro and cons. Good practices when using closures(anonymus methods) in Delphi Delphi代码完成失败,使用匿名方法 - Delphi code completion fail with anonymous methods Delphi 2010:任何人都能获得格式化程序的正确设置,不能折叠匿名方法吗? - Delphi 2010: anyone got the right settings for the formatter not to fold anonymous methods? 在Delphi 2009中使用泛型和匿名方法有什么问题? - What problems are there using generics and anonymous methods in Delphi 2009? 在OTL中使用变量时,如何通过匿名方法捕获变量? - How can I capture variables by anonymous method when using it in OTL? 如何在Delphi的SimpleDataSet中利用Oracle绑定变量? - How can I utilize Oracle bind variables with Delphi's SimpleDataSet? 是否可以捕获Delphi匿名方法/闭包中局部变量的值? - Is it possible to capture the values of variables local to a Delphi anonymous method/closure? 如何使用 Delphi 和 TestInsight 进行匿名测试? - How to do a test with an anonymous with Delphi and TestInsight? 如何在应用程序关闭时终止 Delphi 中的匿名线程? - How to terminate anonymous threads in Delphi on application close? 在Delphi中初始化哪些变量? - Which variables are initialized when in Delphi?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM