Cuando tiene una función como la de la pregunta, donde tiene un método anónimo para acceder a una variable local, Delphi parece crear un descendiente TInterfacedObject que captura todas las variables basadas en la pila como sus propias variables públicas. Usando el truco de Barry para llegar al TObject de implementación y un poco de RTTI, podemos ver todo esto en acción.
El código magia detrás de la puesta en práctica probablemente se parece a esto:
// 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;
Por supuesto, este código no compila. No tengo magia :-) Pero la idea aquí es que se crea un objeto "mágico" detrás de escena y las variables locales que se referencian desde el método anónimo se transforman en campos públicos del objeto mágico. Ese objeto se usa como una interfaz (IUnkown) para que se cuente como referencia. Aparentemente, el mismo objeto captura todas las variables usadas Y define todos los métodos anónimos.
Esto debería responder tanto a "Cuando" como a "Cómo".
Aquí está el código que solía investigar. Ponga un TButton en una forma en blanco, esta debería ser la unidad completa. Cuando se pulsa el botón verá la siguiente información en pantalla, en secuencia:
- 000000 (número de falsos)
- 000000 (el mismo número): Esta pruebas tanto los métodos anónimos sean implementadas como métodos del mismo objeto!
TForm25.Button1Click$ActRec: TInterfacedObject
: Esto muestra el objeto detrás de la aplicación, se deriva de TInterfacedObject
OnStack:string
: RTTI descubre este campo en ese objeto.
Self: TForm25
: RTTI descubre este campo en ese objeto. Se utiliza para obtener el valor de ClasVar
FRefCount:Integer
- esto viene de TInterfacedObject
Class Var
- resultado de ShowMessage.
On Stack
- resultado de ShowMessage.
Aquí está el código:
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.
desplazamiento a lo que también es más útil para la comunidad. (Y, por supuesto, ¡obtienes mucho más reputación publicando aquí! :)) –
Hm ... ¿Las funciones/procedimientos se llaman "métodos" si no pertenecen a un objeto? –
que versión delphi estás usando? – kiw