Class 制作Delphi事件类方法

Class 制作Delphi事件类方法,class,delphi,events,methods,Class,Delphi,Events,Methods,有时我会有一个随机的想法,这是我最近的想法。控制事件通常不引用它们所附加到的表单/数据模块,这在某种程度上符合类方法的定义。除了IDE的抱怨(这是一个非常令人讨厌的问题)之外,还有什么技术原因可以解释为什么人们不这么做 例如: class procedure TForm9.Button1Click(Sender: TObject); begin ShowMessage('Hello World'); end; 我简单地尝试了一下,似乎很有效。您不能使用表单设计器或对象检查器绑定它们,但可以

有时我会有一个随机的想法,这是我最近的想法。控制事件通常不引用它们所附加到的表单/数据模块,这在某种程度上符合类方法的定义。除了IDE的抱怨(这是一个非常令人讨厌的问题)之外,还有什么技术原因可以解释为什么人们不这么做

例如:

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

我简单地尝试了一下,似乎很有效。

您不能使用表单设计器或对象检查器绑定它们,但可以直接分配它们:

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;
将显示:
按钮1向TForm9问好


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

您不能使用表单设计器或对象检查器绑定它们,但您可以直接分配它们:

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;
将显示:
按钮1向TForm9问好


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

非静态类方法有一个
Self
指针。那
Self
就是这个类。非静态类方法有一个
Self
,以支持虚拟类方法的分派

因为非静态类方法具有
Self
,这意味着可以根据事件处理程序的需要将它们分配给对象的
方法类型。因此,您当然可以用Pascal代码编写运行时分配:

Button1.OnClick := TForm1.SomeClassMethod;
但是,IDE不支持类方法作为事件处理程序。我相信这是有充分理由的。当流代码遇到该设置时,在.dfm文件中显示如下:

OnClick = SomeClassMethod
只有在手动输入.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
,一个实例而不是类


流式代码无法正确连接类方法。因此,设计器不会提供用作事件处理程序的类方法。但从代码中,您可以很好地将类方法分配给事件处理程序,并且一切正常

非静态类方法有一个
Self
指针。那
Self
就是这个类。非静态类方法有一个
Self
,以支持虚拟类方法的分派

因为非静态类方法具有
Self
,这意味着可以根据事件处理程序的需要将它们分配给对象的
方法类型。因此,您当然可以用Pascal代码编写运行时分配:

Button1.OnClick := TForm1.SomeClassMethod;
但是,IDE不支持类方法作为事件处理程序。我相信这是有充分理由的。当流代码遇到该设置时,在.dfm文件中显示如下:

OnClick = SomeClassMethod
只有在手动输入.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
,一个实例而不是类


流式代码无法正确连接类方法。因此,设计器不会提供用作事件处理程序的类方法。但从代码中,您可以很好地将类方法分配给事件处理程序,并且一切正常

重点是什么?您不能使用任何变量/控件,那么为什么要将其作为可视容器的一部分呢?您无法访问
发件人
,因此只能将其附加到单个按钮上(必须首先存在该按钮才能单击,因此没有理由将其作为
类过程
)。@KenW