代码之家  ›  专栏  ›  技术社区  ›  James

在没有类类型的反序列化窗体上将方法作为参数传递

  •  1
  • James  · 技术社区  · 16 年前

    我正在有效地尝试反序列化表单。

    序列化窗体上的一个对象具有一个方法,该方法将一系列事件作为参数。

    现在,因为在反序列化时没有对象的类类型,所以在对象上有一个方法执行反序列化,称为addmethod,声明如下:

    procedure TMyDeserializer.AddMethod(ControlName, EventName: String;
      MethodAddr: Pointer);
    var
        TargetControl : TControl;
        Method : TMethod;
    begin
        if Not Assigned(TempForm) then
            Exit;
        if TempForm.Name = ControlName then
            TargetControl := TempForm
        else
            TargetControl := TempForm.FindChildControl(ControlName);
    
        if Assigned(TargetControl) then
        begin
            Method.Code := MethodAddr;
            Method.Data := TargetControl;
            SetMethodProp(TargetControl, EventName, Method);
        end;
    end;
    

    为了在反序列化子例程时将它们插入到各种控件中,问题是需要将事件添加为参数列表(而不是添加到控件中)。例如

    SetUpEvents(EventHandler1:TNotifyEvent;EventHandler2:TNotifyEvent);
    

    其中eventhandler1和eventhandler2在代码中定义为

    Procedure EventHandler1(Sender:TNotifyEvent);  
    begin
        // Do something
    end;
    

    这些不是方法,而是独立的子例程。

    当我将这些分配给对象时,子例程不需要成为对象的一部分,因为addmethod过程使用类似

    MyDeserializerInstance.AddMethod('Button1','OnClick',@EventHandler1);
    

    这适用于标准事件处理程序,如button1.onclick,但如果我想这样做,则不适用。

    Procedure SetUpButton1Click(Method: TNotifyEvent)
    begin
        TButton(MyDeserializerInstance.TempForm.FindChildControl('Button1')).OnClick = Method;
    end;
    

    问题是我不能将子例程作为方法传递给示例设置过程。

    正在创建的表单没有在接口中声明,而是完全由它所读取的文件以及代码中的几个独立例程定义。

    所以我想问题是,如何在运行时(在创建了一个对象之后)将一个子例程转换成一个方法,如果我不能这样做,我如何在代码中将子例程作为参数传递给另一个方法?

    到目前为止,我尝试将tmethod强制转换为正确的事件类型,并将.data填充为tempform。它调用了正确的方法,但它扰乱了参数。

    Delphi版本是2007

    3 回复  |  直到 16 年前
        1
  •  4
  •   Remy Lebeau    16 年前

    非静态类方法具有隐藏的 Self 调用方法时填充的输入参数。这就是 TMethod.Data 字段对应于。为了将独立过程用作需要类方法的事件的处理程序,该过程必须定义一个额外的参数来表示 自我 参数so的值 方法数据 有地方可去,即:

    procedure Button1ClickHandler(Self: Pointer; Sender: TObject);
    begin 
      // Do something 
    end; 
    
    MyDeserializerInstance.AddMethod('Button1', 'OnClick', @Button1ClickHandler);
    

    您的addMethod()实现正在将TargetControl指定为 方法数据 值,所以 自我 Sender 上面的参数将在运行时指向同一个对象,但这是可以的。

    没有明确的 自我 参数定义,这解释了为什么在运行时调用过程时参数变得“混乱”。隐藏的 自我 正在将值分配给 发件人 参数,和实数 发件人 值被忽略。

        2
  •  0
  •   Kenneth Cochran    16 年前

    如果我错了,肯定会有人纠正我,但我不相信有一种方法可以在运行时在本机Delphi中创建类型定义。德尔福的RTTI还不能解决这个问题。

    对象序列化的两个场景是持久性和IPC。(可能还有更多我没想到的)。

    Delphi的DFM序列化就是持久性的一个例子。如果你看一个DFM,你会发现它根本没有定义方法。它只是将事件处理程序分配给需要事件处理程序的属性。处理程序和属性都是在设计时使用普通类型定义定义的。

    如果您的意图是IPC(无论是在同一台机器上还是在远程机器上),那么已经有了实现这一点的框架。( RemObjects 铭记在心)

        3
  •  0
  •   Rob Kennedy    16 年前

    在运行时您不会“创建方法”。这相当于编译新代码。分配给各种事件属性的方法必须已经存在。

    此外,不能“添加事件”。正在反序列化的对象已经有事件。在Delphi代码中编写类声明时定义了它们。编译类后,不能将新的事件属性添加到类中。

    看来你真正想说的是 独立过程 你恰巧说出了 Method1 你想把它当作 TNotifyEvent 调用时的参数 SetUpMethods .

    那是错误的做法。那 方法1 过程不是一个方法,尽管它的名称是,所以不能在需要方法的地方使用它。把声明改成 属于一个类 ,然后它将是一种方法。

    如果您不想实例化该方法所属的类,那么可以将其声明为 类方法 而是:

    class procedure TSomeClass.Method1(Sender: TNotifyEvent);
    

    我鼓励你改变 AddMethod 使最后一个参数的类型为 TMethod . 然后,您就可以同时拥有方法指针的代码和数据部分。现在,您正在基于要分配其事件属性的对象分配数据部分,但是正如我在我的注释中提到的,这种关系很少存在,特别是现在,该方法属于完全不相关的类( TSomeClass 在我的例子中)。的值 TMethod.Data 字段变为 Self 方法被调用时的值。您有责任确保存储在该字段中的值与代码所属的类具有兼容类型。