制作Delphi事件类方法

时间:2014-02-11 23:43:13

标签: class delphi events methods

有时我会随机思考,这是我最新的。通常,控件事件不会引用它们所附加的表单/数据模块,这有点符合类方法的定义。除了IDE抱怨(这是一个非常令人讨厌的showstopper),有没有任何技术上的原因,为什么一个人不会这样做。

例如:

class procedure TForm9.Button1Click(Sender: TObject);
begin
  ShowMessage('Hello World');
end;

我曾经简单地试过这个并且似乎有效。

2 个答案:

答案 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

如果控件和事件处理程序在同一个类中可能没有多大意义,因为这个类需要有一个实例才能使控件触发事件,但在很多情况下它们是不同的类/实例使用事件连接在一起。