2008-12-11 13 views

Odpowiedz

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

end)^) + $0C)^; 

works w Delphi 2010

+1

@Majin: To jest sprytne i działa, ponieważ anonimowa metoda jest osadzona w niejawnej klasie. Koniecznie przeczytaj komentarz Barry'ego Kelly w cytowanym artykule na temat przyszłej kompatybilności, a także ten post Barry'ego Kelly: http://blog.barrkel.com/2010/01/using-anonymous-methods-in-method.html –

+1

To rozwiązanie działa na platformie Win32, ale nie na Win64. –

+1

@ChauCheeYang Najprawdopodobniej dlatego, że $ 0C powinno być SizeOf (Pointer) * 3 .. –

5

Doskonałe pytanie.

O ile mi wiadomo, nie można tego zrobić w aktualnej wersji Delphi. Jest to bardzo niefortunne, ponieważ te anonimowe procedury byłyby wspaniałe do szybkiego skonfigurowania obsługi zdarzeń obiektu, na przykład podczas konfigurowania urządzeń testowych w strukturze automatycznego testowania xUnit.

Nie powinno być dwa sposoby na CodeGear do wdrożenia tej funkcji:

1: Zezwalaj na tworzenie metod anonimowych. Coś takiego:

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

Problem polega na tym, co należy umieścić jako wskaźnik dla metody anonimowej. Można użyć wskaźnika własnego obiektu, z którego została utworzona anonimowa metoda, ale można tworzyć tylko anonimowe metody z kontekstu obiektu. Lepszym pomysłem może być po prostu stworzenie obojętnego obiektu za kulisami, aby zawierał anonimową metodę.

2: Alternatywnie, można pozwolić typom zdarzeń na akceptowanie zarówno metod, jak i procedur, o ile mają one wspólny podpis. W ten sposób możesz stworzyć obsługę zdarzeń tak, jak chcesz:

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

W moich oczach jest to najlepsze rozwiązanie.

+0

Metoda nie ma ten sam podpis jako procedura rozmów z tymi samymi argumentami. Metody zawsze przekazują Self jako "ukryty" argument. –

+1

Tak, oczywiście.Ale nie widzę powodu, dla którego kompilator nie byłby w stanie obsłużyć obu przypadków "za kulisami". Może na przykład utworzyć obojętne opakowanie klasy wokół anonimowej procedury, jeśli oczekiwana jest metoda. –

+1

Ten wzorzec jest tak powszechny w językach dynamicznych, takich jak JavaScript Python. –

4

W poprzednich wersjach Delphi można użyć zwykłej procedury jak obsługi zdarzeń dodając ukryty wskaźnik samodzielne parametrów i ciężko typecast go:

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; 

nie jestem pewien wyżej naprawdę kompiluje ale powinno dać masz ogólny pomysł. Zrobiłem to wcześniej i zadziałało to w przypadku regularnych procedur. Ponieważ nie wiem, jaki kod generuje kompilator dla zamknięć, nie mogę powiedzieć, czy to zadziała.

+0

Właśnie to przetestowałem i nie zadziałało to z zamknięciem, ale być może coś przeoczyłem. –

1

Jej łatwo rozszerzyć poniżej obsłużyć więcej typów zdarzeń formularza.

Wykorzystanie

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; 

Kod

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.