2008-12-11 16 views

Respuesta

10
@Button1.OnClick := pPointer(Cardinal(pPointer(procedure (sender: tObject) 
begin 
    ((sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!' 

end)^) + $0C)^; 

works en Delphi 2010

+1

@Majin: esto es inteligente y funciona porque un método anónimo está incrustado en una clase implícita. Asegúrese de leer el comentario de Barry Kelly en el artículo al que se hace referencia sobre compatibilidad futura, y esta publicación de Barry Kelly: http://blog.barrkel.com/2010/01/using-anonymous-methods-in-method.html –

+1

Esta solución funciona en la plataforma Win32 pero no en Win64. –

+1

@ChauCheeYang Probablemente porque los $ 0C deben ser SizeOf (Pointer) * 3 .. –

5

Una excelente pregunta.

Por lo que yo sé, no es posible hacer en la versión actual de Delphi. Esto es muy desafortunado ya que esos procedimientos anónimos serían geniales para configurar rápidamente los manejadores de eventos de un objeto, por ejemplo cuando se configuran los dispositivos de prueba en un tipo de marco de prueba automático xUnit.

Debe haber dos maneras de CodeGear Para implementar esta función:

1: Permitir la creación de métodos anónimos. Algo como esto:

Button1.OnClick := procedure(sender : tobject) of object begin 
    ... 
end; 

El problema aquí es qué poner como el auto puntero para el método anónimo. Uno podría usar el puntero del objeto desde el que se creó el método anónimo, pero luego solo se pueden crear métodos anónimos desde un contexto de objeto. Una mejor idea podría ser simplemente crear un objeto falso detrás de las escenas para contener el método anónimo.

2: Alternativamente, se podría permitir que los tipos de eventos acepten tanto métodos como procedimientos, siempre que compartan la firma definida. De esta forma, puede crear el controlador de eventos de la manera que desee:

Button1.OnClick := procedure(sender : tobject) begin 
    ... 
end; 

En mi opinión, esta es la mejor solución.

+0

Un método no tiene la misma firma llamar como un procedimiento con los mismos argumentos. Los métodos siempre pasan al Ser como un argumento "oculto". –

+1

Sí, por supuesto.Pero no veo ninguna razón por la cual el compilador no debería ser capaz de manejar ambos casos "detrás de escena". Por ejemplo, podría crear una envoltura de clase ficticia alrededor del procedimiento anónimo si se espera un método. –

+1

Este patrón es muy común en lenguajes dinámicos como JavaScript Python. –

4

En las versiones anteriores de Delphi se puede utilizar un procedimiento regular como controlador de eventos añadiendo el puntero del mismo ocultado a los parámetros y duro encasillado que:

procedure MyFakeMethod(_self: pointer; _Sender: TObject); 
begin 
    // do not access _self here! It is not valid 
    ... 
end; 

... 

var 
    Meth: TMethod; 
begin 
    Meth.Data := nil; 
    Meth.Code := @MyFakeMethod; 
    Button1.OnClick := TNotifyEvent(Meth); 
end; 

No estoy seguro de lo anterior realmente compila pero debería dar usted la idea general. He hecho esto previamente y funcionó para procedimientos regulares. Como no sé qué código genera el compilador para los cierres, no puedo decir si esto funcionará para ellos.

+0

Acabo de probar eso, y no funcionó con un cierre, pero puede haberme perdido algo. –

1

Su fácil de extender el siguiente para manejar más tipos de eventos de formulario.

Uso

procedure TForm36.Button2Click(Sender: TObject); 
var 
    Win: TForm; 
begin 
    Win:= TForm.Create(Self); 
    Win.OnClick:= TEventComponent.NotifyEvent(Win, procedure begin ShowMessage('Hello'); Win.Free; end); 
    Win.Show; 
end; 

Código

unit AnonEvents; 

interface 
uses 
    SysUtils, Classes; 

type 
    TEventComponent = class(TComponent) 
    protected 
    FAnon: TProc; 
    procedure Notify(Sender: TObject); 
    class function MakeComponent(const AOwner: TComponent; const AProc: TProc): TEventComponent; 
    public 
    class function NotifyEvent(const AOwner: TComponent; const AProc: TProc): TNotifyEvent; 
    end; 

implementation 

{ TEventComponent } 

class function TEventComponent.MakeComponent(const AOwner: TComponent; 
    const AProc: TProc): TEventComponent; 
begin 
    Result:= TEventComponent.Create(AOwner); 
    Result.FAnon:= AProc; 
end; 

procedure TEventComponent.Notify(Sender: TObject); 
begin 
    FAnon(); 
end; 

class function TEventComponent.NotifyEvent(const AOwner: TComponent; 
    const AProc: TProc): TNotifyEvent; 
begin 
    Result:= MakeComponent(AOwner, AProc).Notify; 
end; 

end. 
Cuestiones relacionadas