有时我会随机思考,这是我最新的。通常,控件事件不会引用它们所附加的表单/数据模块,这有点符合类方法的定义。除了IDE抱怨(这是一个非常令人讨厌的showstopper),有没有任何技术上的原因,为什么一个人不会这样做。
例如:
class procedure TForm9.Button1Click(Sender: TObject);
begin
ShowMessage('Hello World');
end;
我曾经简单地试过这个并且似乎有效。
答案 0 :(得分:8)
非静态类方法有一个Self
指针。 Self
是班级。非静态类方法具有Self
以支持虚拟类方法的分派。
因为非静态类方法具有Self
,这意味着可以根据事件处理程序的需要将它们分配给of object
方法类型。因此,您当然可以在Pascal代码中编写运行时赋值:
Button1.OnClick := TForm1.SomeClassMethod;
但是,IDE不支持将类方法作为事件处理程序。我相信这是有充分理由的。当流代码遇到设置时,它在.dfm文件中显示如下:
OnClick = SomeClassMethod
当您手动将其放入.dfm文件时,它只能出现在.dfm文件中。当你说IDE抱怨时,这就是我认为你的意思。您将上述内容添加到.dfm文件中,每当您加载表单时,IDE都会将其拒绝为无效。
现在,无论如何,假设你这样做了。然后,流代码遇到上述.dfm设置为文本。它需要使用RTTI来查找SomeClassMethod
。哪个好。然后它从数据指针和代码指针合成方法值。它使用实例作为数据指针。这意味着该方法无效。您不能将实例和类方法配对。但是流代码并不是更好,而是建立在.dfm文件中找到的方法是实例方法的假设之上。假设设计者仅提供实例方法,则给出合理的假设。
可以在TReader.FindMethodInstance
:
function TReader.FindMethodInstance(Root: TComponent; const MethodName: string): TMethod;
var
Error: Boolean;
begin
if Assigned(FOnFindMethodInstance) then
begin
Result.Code := Root.MethodAddress(MethodName);
Result.Data := Root;
Error := Result.Code = nil;
FOnFindMethodInstance(Self, MethodName, Result, Error);
end else
Error := True;
if Error then
begin
Result.Data := Root;
Result.Code := FindMethod(Root, MethodName);
end;
end;
相关代码是将Root
分配给Result.Data
,因为Root
是实例。
因此,当事件触发时,您的方法会运行,但任何使用Self
的尝试都会失败。因为流代码给你一个无效的Self
,一个实例而不是一个类。
流代码无法正确连接类方法。因此,设计者不会提供用作事件处理程序的类方法。但是从代码中你可以很好地将类方法分配给事件处理程序,一切正常。
答案 1 :(得分:6)
您无法使用表单设计器或对象检查器绑定它们,但您可以直接指定它们:
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.OnClick := TForm1.Button1Click;
end;
class procedure TForm9.Button1Click(Sender: TObject);
begin
ShowMessageFmt('%s says hello %s', [(Sender as TComponent).Name, Self.ClassName]);
end;
将显示:Button1 says hello TForm9
如果控件和事件处理程序在同一个类中可能没有多大意义,因为这个类需要有一个实例才能使控件触发事件,但在很多情况下它们是不同的类/实例使用事件连接在一起。