在没有 ClassType 的反序列化表单上将方法作为参数传递
我正在有效地尝试反序列化表单。
序列化形式上的一个对象有一个方法,该方法将一系列事件作为参数。
现在,由于我在反序列化时没有对象的类类型,因此我在对象上有一个执行反序列化的方法,称为 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
I'm effectively trying to deserialize a form.
One of the objects on the serialized form has a method which takes a series of events as parameters.
Now since I don't have the class type of the object when I'm deserializing, I have a method on the object doing the deserialization called AddMethod which is declared like this:
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;
So that I can poke subroutines into the various controls as I deserialize them, The problem is I need to add events as a list of parameters (not to a control). e.g.
SetUpEvents(EventHandler1:TNotifyEvent;EventHandler2:TNotifyEvent);
Where EventHandler1 and EventHandler2 are defined somewhere in code as
Procedure EventHandler1(Sender:TNotifyEvent);
begin
// Do something
end;
These are not methods but stand alone subroutines.
When I'm assigning these to objects the subroutine doesn't need to be part of an object as the AddMethod procedure handles it with a call like
MyDeserializerInstance.AddMethod('Button1','OnClick',@EventHandler1);
This works for standard event handlers, such as Button1.OnClick but not if I want to do
Procedure SetUpButton1Click(Method: TNotifyEvent)
begin
TButton(MyDeserializerInstance.TempForm.FindChildControl('Button1')).OnClick = Method;
end;
The problem is I can't pass the subroutine as a method to the example Set Up Procedure.
The form being created isn't declared in an interface and is entirely defined by the file it is read from as well as a few stand alone routines in code.
So I suppose the question is how do turn a subroutine into a method at run time (after creating the object it is supposed to be part of), and if I can't do that how do I pass the subroutines in code as parameters in another method?
So far I've tried casting a TMethod as the correct event type and filling in the .Data as the TempForm. It called the correct method but it scrambled the parameters.
Delphi version is 2007
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
非静态类方法有一个隐藏的
Self
输入参数,该参数在调用方法时填充。这就是TMethod.Data
字段所对应的内容。为了使用独立过程作为需要类方法的事件的处理程序,该过程必须定义一个额外的参数来表示Self
参数,以便TMethod.Data< 的值/code> 有地方可去,即:
您的 AddMethod() 实现将 TargetControl 分配为
TMethod.Data
值,因此Self
和Sender 参数最终会在运行时指向同一个对象,但这没关系。
如果没有定义显式的 Self 参数,这就解释了为什么在运行时调用过程时参数会变得“混乱”。隐藏的
Self
值被分配给Sender
参数,而真正的Sender
值被忽略。Non-static class methods have a hidden
Self
input parameter that is filled in when the method is called. That is what theTMethod.Data
field corresponds to. In order to use a standalone procedure as a handler for an event that expects a class method, the procedure must have an extra parameter defined to represent theSelf
parameter so the value ofTMethod.Data
has somewhere to go, ie:Your AddMethod() implementation is assigning the TargetControl as the
TMethod.Data
value, so theSelf
andSender
parameters above will end up pointing at the same object at runtime, but that is OK.Without the explicit
Self
parameter defined, that explains why your parameters are getting "scrambled" when the procedure called at runtime. The hiddenSelf
value is being assigned to theSender
parameter, and the realSender
value is being ignored.我确信如果我错了,有人会纠正我,但我不相信有一种方法可以在本机 Delphi 中在运行时创建类型定义。 Delphi 的 RTTI 还不能处理这个问题。
对于对象序列化,我想到的两个场景是持久性和 IPC。 (可能还有更多我没想到的)。
Delphi 的 DFM 序列化就是持久性的一个例子。如果您查看 dfm,您会发现它根本没有定义方法。它只是将事件处理程序分配给需要事件处理程序的属性。处理程序和属性都是在设计时使用普通类型定义定义的。
如果您的意图是 IPC(无论是在同一台计算机上还是在远程计算机上),则已经有现有的框架可以实现此目的。 (RemObjects 浮现在脑海中)
I'm sure someone will correct me if I'm wrong but I don't believe there is a way to create a type definition at runtime in native Delphi. Delphi's RTTI just doesn't handle this yet.
The two scenarios that come to mind for object serialization are persistence and IPC. (There may be more that I haven't thought of).
Delphi's DFM serialization would be an example of persistence. If you look at a dfm you'll notice it isn't defining methods at all. It's simply assigning event handlers to properties expecting an event handler. Both the handlers and the properties are defined at design time using normal type definitions.
If your intent is IPC(whether on the same machine or a remote one) there are already existing frameworks for accomplishing this. (RemObjects comes to mind)
您不会在运行时“创建方法”。这相当于编译新代码。您分配给各种事件属性的方法需要已经存在。
此外,您无法“添加事件”。您要反序列化的对象已经有事件。当您在 Delphi 代码中编写类声明时就定义了它们。编译后的类无法添加新的事件属性。
看来您真正想说的是您有一个独立过程,您碰巧将其命名为
Method1
,并且您希望将其作为TNotifyEvent 传递调用
参数。SetUpMethods
时的这是错误的做法。尽管
Method1
过程的名称如此,但它并不是一个方法,因此您不能在需要方法的地方使用它。更改该声明,使其属于一个类,然后它将成为一个方法。如果您不想实例化该方法所属的类,那没问题 - 您可以将其声明为类方法:
我鼓励您更改
AddMethod 的声明
以便最后一个参数的类型为TMethod
。那么您肯定拥有方法指针的代码和数据部分。现在,您正在根据要分配其事件属性的对象来分配数据部分,但正如我在评论中提到的,这种关系很少存在,特别是现在该方法属于完全不相关的类(在我的示例中为TSomeClass
)。当方法被调用时,TMethod.Data
字段的值将成为Self
值。您有责任确保存储在该字段中的值的类型与代码所属的类兼容。You don't "make a method" at run time. That would amount to compiling new code. The methods that you assign to various event properties need to already exist.
Furthermore, you can't "add events." The object you're deserializing already has events. You defined them when you wrote the class declaration in your Delphi code. You can't add new event properties to a class after it's been compiled.
It appears that what you're really saying is that you have a standalone procedure that you happen to have named
Method1
, and you want to pass it as aTNotifyEvent
parameter when you callSetUpMethods
.That's the wrong way to go. That
Method1
procedure isn't a method, despite its name, so you mustn't use it where a method is required. Change that declaration so it belongs to a class, and then it will be a method.If you don't want to have to instantiate the class that the method belongs to, that's fine — you can declare it as a class method instead:
I encourage you to change the declaration of
AddMethod
so that the last parameter is of typeTMethod
. Then you're sure to have both the code and data portions of the method pointer. Right now, you're assigning the data portion based on the object whose event property you're assigning, but as I mentioned in my comment, it's rare for that relationship to exist, especially now that the method belongs to an entirely unrelated class (TSomeClass
in my example). The value of theTMethod.Data
field becomes theSelf
value when the method gets called. It's your responsibility to ensure that the value you store in that field is of a compatible type for the class the code belongs to.