usar tipo runtimebinderexception runtimebinder que puede parametros para microsoft metodos invocar funciones funcion expresiones delegados delegado csharp con anonimos anonimas delphi delphi-2010 anonymous-methods

delphi - que - microsoft csharp runtimebinder runtimebinderexception no se puede invocar un tipo no delegado



¿Cómo y cuándo se capturan las variables a las que se hace referencia en los métodos anónimos de Delphi? (1)

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 de objeto de interfaz 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 mágico detrás de la implementación probablemente se vea así:

// 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 a las que se hace referencia desde el método anónimo se transforman en campos públicos del objeto mágico. Ese objeto se usa como una interfaz (IUnkown) por lo que se cuenta como referencia. Aparentemente, el mismo objeto captura todas las variables utilizadas Y define todos los métodos anónimos.

Esto debería responder tanto al "cuándo" como al "cómo".

Aquí está el código que solía investigar. Coloque un TButton en un formulario en blanco, esta debe ser la unidad completa. Cuando presione el botón, verá lo siguiente en la pantalla, en secuencia:

  • 000000 (número falso)
  • 000000 (el mismo número): ¡Estas pruebas de ambos métodos anónimos se implementan como métodos del mismo objeto!
  • TForm25.Button1Click$ActRec: TInterfacedObject : Esto muestra el objeto detrás de la implementación, se deriva de TInterfacedObject
  • OnStack:string : RTTI descubre este campo en ese objeto.
  • Self: TForm25 : RTTI descubre este campo en ese objeto. Se usa 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.

Esto fue sugerido por ¿Cómo comparar TFunc / TProc que contiene la función / procedimiento del objeto? , específicamente por el comentario de David a la pregunta de Barry. Ya que no tengo un blog para publicar esto, voy a hacer esta pregunta aquí y responderla.

Pregunta : ¿Cuándo y cómo se capturan las variables a las que se hace referencia en los métodos anónimos de Delphi?

Ejemplo:

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;

Ambos ShowMessage van a mostrar 2 . ¿Por qué? ¿Cómo se captura V y cuándo?