2012-07-15 53 views
14

进一步就此post其接受的答案仍然很神秘:传输处理器<TObject>到TNotifyEvent

@Button1.OnClick := pPointer(Cardinal(pPointer(procedure (sender: tObject) begin ((sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!' end)^) + $0C)^; 

不知羯羊它是可能设计出一个简单而优雅的方式类似于:

Button.OnClick := 
        AnonProc2NotifyEvent (
        procedure (Sender: TObject) 
        begin 
         ((Sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!' 
        end 
        ); 

,从而以达到相同的目的,并且AnonProc2NotifyEvent是Button的拥有者的方法,并具有以下签名:

TOwnerOfButton = class(TForm) 
    Button: TButton; 
    ... 
private 
    ... 
protected 
    function AnonProc2NotifyEvent(aProc: TProc<TObject>): TNotifyEvent; 
public 
    ... 
end; 

这是可行的,如果是的话如何实现它?

+0

您可能想看看[DSharp.Core.Events.pas](http://code.google.com/p/delphisorcery/source/browse/trunk/Source/Core/DSharp.Core.Events .PAS)。 – 2012-07-15 14:11:22

+0

@Stefan Glienke:谢谢你提醒我,我确实在我的盒子上安装了DSharp,我错过了它(对我来说,我经常不经常使用它),但是相信自从它诞生以来就遵循它(Bwt I我也是DelphiPraxis的成员,不擅长德语,但努力关注德国德尔福的繁荣景象)。肯定泛型是我的下一步。谢谢* Stevie * :-) – menjaraz 2012-07-15 14:27:15

回答

21

这将做的工作很容易就够了:

type 
    TNotifyEventWrapper = class(TComponent) 
    private 
    FProc: TProc<TObject>; 
    public 
    constructor Create(Owner: TComponent; Proc: TProc<TObject>); 
    published 
    procedure Event(Sender: TObject); 
    end; 

constructor TNotifyEventWrapper.Create(Owner: TComponent; Proc: TProc<TObject>); 
begin 
    inherited Create(Owner); 
    FProc := Proc; 
end; 

procedure TNotifyEventWrapper.Event(Sender: TObject); 
begin 
    FProc(Sender); 
end; 

function AnonProc2NotifyEvent(Owner: TComponent; Proc: TProc<TObject>): TNotifyEvent; 
begin 
    Result := TNotifyEventWrapper.Create(Owner, Proc).Event; 
end; 

AnonProc2NotifyEventOwner参数是使包装对象的生命周期进行管理。没有这样的东西,你会泄漏TNotifyEventWrapper的实例。

通过为Owner,您正在连接事件的组件。例如:

Button1.OnClick := AnonProc2NotifyEvent(
    Button1, 
    procedure(Sender: TObject) 
    begin 
    (Sender as TButton).Caption := 'Clicked'; 
    end 
); 

所以,当按钮被销毁时,TNotifyEventWrapper也将被销毁。包装器对象的生存期至少与其关联事件的对象一样长。因此,作为所有者的Button1的选择是自然而明显的选择。

+0

谢谢大卫!它永远不会让我想到封装将会做什么,并且它是组件化的好候选者:简明,干净和优雅的解决方案。 – menjaraz 2012-07-15 13:39:28

5

仅供参考这就是我在得到,我研究Barry Kelly的博客post在上述的现有SO后引用,并且该解决方案提出了:

function TMainForm.Proc2NotifyEvent(const aProc: TNotifyReference): TNotifyEvent; 
type 
    TVtable = array[0..3] of Pointer; 
    PVtable = ^TVtable; 
    PPVtable = ^PVtable; 
begin 
    TMethod(Result).Code := PPVtable((@aProc)^)^^[3]; 
    TMethod(Result).Data := Pointer((@aProc)^); 
end; 

仍然神秘,但微胶囊,因此缓解了编码器的任务与初始方法相比较。

我试图整理MethRefToMethPtrMakeNotify,并把它们都放在一个方法。

注意,有一个在方法的签名(轻微)的变化,参数aProc成为常量

+1

+1这是邪恶的! – ComputerSaysNo 2012-07-15 14:08:54

+0

你仍然需要做一些事情来保持实现anon proc接口的对象。巴里确实提到了这个重要的细节。 – 2012-07-15 14:21:56

+0

@David Heffernan:我应该在某处保留一个引用吗? – menjaraz 2012-07-15 14:34:16