匿名方法是如何在幕后实现的? [英] How are anonymous methods implemented under the hood?

查看:16
本文介绍了匿名方法是如何在幕后实现的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Delphi 是否实例化"了每个匿名方法(如对象)?如果是,Delphi 什么时候创建这个实例,最重要的是,Delphi 什么时候释放它?

Does Delphi "instantiate" each anonymous method (like an object)?, if so when does Delphi create this instance, and most important, when does Delphi free it?

因为匿名方法也会捕获外部变量并延长它们的生命周期,所以了解这些变量何时会从内存中释放"很重要.

Because anonymous method also captures external variables and extends their life time, it's important to know when these variables will be "released" from the memory.

在另一个匿名方法中声明一个匿名方法可能有哪些缺点.是否可以循环引用?

What are the possible drawbacks to declare an anonymous method inside another anonymous method. Are circular reference possible?

推荐答案

匿名方法被实现为接口.这篇文章很好地解释了编译器是如何完成的:Delphi 中的匿名方法:内部结构.

Anonymous methods are implemented as interfaces. This article has a good explanation of how it is done by the compiler: Anonymous methods in Delphi: the internals.

本质上,编译器生成的接口有一个名为Invoke的方法,后面是你提供的匿名方法.

In essence, the compiler generated interface has a single method named Invoke, behind which is the anonymous method that you provide.

捕获的变量与捕获它们的任何匿名方法具有相同的生命周期.匿名方法是一个接口,它的生命周期由引用计数管理.因此,被捕获变量的生命周期与捕获它们的匿名方法一样长.

Captured variables have the same lifetime as any anonymous methods that capture them. The anonymous method is an interface and its lifetime is managed by reference counting. Therefore, the captured variables life extends as long as does the anonymous methods that capture them.

正如可以使用接口创建循环引用一样,也必须同样可以使用匿名方法创建循环引用.这是我可以构建的最简单的演示:

Just as it is possible for circular references to be created with interfaces, it must equally be possible for circular references to be created with anonymous methods. Here is the simplest demonstration that I can construct:

uses
  System.SysUtils;

procedure Main;
var
  proc: TProc;
begin
  proc :=
    procedure
    begin
      if Assigned(proc) then
        Beep;
    end;
end;

begin
  ReportMemoryLeaksOnShutdown := True;
  Main;
end.

编译器在幕后创建了一个实现匿名方法接口的隐藏类.该类包含捕获的任何变量作为数据成员.当分配给 proc 时,会增加实现实例的引用计数.由于 proc 归实现实例所有,因此该实例已经引用了自身.

Behind the scenes the compiler creates a hidden class that implements the anonymous method interface. That class contains as data members any variables that are captured. When proc is assigned to, that increases the reference count on the implementing instance. Since proc is owned by the implementing instance, that instance therefore has taken a reference to itself.

为了更清楚地说明这一点,该程序提出了相同的问题,但在接口方面进行了重新调整:

To make this a little clearer, this program presents the identical issue but re-cast in terms of interfaces:

uses
  System.SysUtils;

type
  ISetValue = interface
    procedure SetValue(const Value: IInterface);
  end;

  TMyClass = class(TInterfacedObject, ISetValue)
    FValue: IInterface;
    procedure SetValue(const Value: IInterface);
  end;

procedure TMyClass.SetValue(const Value: IInterface);
begin
  FValue := Value;
end;

procedure Main;
var
  intf: ISetValue;
begin
  intf := TMyClass.Create;
  intf.SetValue(intf);
end;

begin
  ReportMemoryLeaksOnShutdown := True;
  Main;
end.

可以通过显式清除自引用来打破循环.在如下所示的匿名方法示例中:

It is possible to break the circularity by explicitly clearing the self-reference. In the anonymous method example that looks like this:

procedure Main;
var
  proc: TProc;
begin
  proc :=
    procedure
    begin
      if Assigned(proc) then
        Beep;
    end;
  proc := nil;
end;

接口变体的等价物是:

procedure Main;
var
  intf: ISetValue;
begin
  intf := TMyClass.Create;
  intf.SetValue(intf);
  intf.SetValue(nil);
end;

这篇关于匿名方法是如何在幕后实现的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆