Delphi 将对象方法添加到stringlist,以便可以按名称调用它们

Delphi 将对象方法添加到stringlist,以便可以按名称调用它们,delphi,Delphi,我有一个服务器代码,它接受来自客户端的命令并执行由接收到的命令确定的对象方法。我想使用AddObject构建一个stringlist,将命令与所需的过程相关联。这在独立过程中可以正常工作,但在尝试将对象方法添加到stringlist时会出现“variable required”错误。下面是示例代码: type TExample = class public var Commands: TStringList; constructor Create; destruc

我有一个服务器代码,它接受来自客户端的命令并执行由接收到的命令确定的对象方法。我想使用AddObject构建一个stringlist,将命令与所需的过程相关联。这在独立过程中可以正常工作,但在尝试将对象方法添加到stringlist时会出现“variable required”错误。下面是示例代码:

type
  TExample = class
  public
    var Commands: TStringList;
    constructor Create;
    destructor Destroy; override;
    procedure ExecCommand(Cmd, Msg: string);
    procedure Alpha(Msg: string);
    procedure Beta(Msg: string);
    procedure Gamma(Msg: string);
  end;

constructor TExample.Create;
begin
  inherited Create;
  Commands := TStringList.Create;
  Commands.AddObject('Alpha', @Alpha); // fails to compile: "variable required"
  Commands.AddObject('Beta', @Beta);
  Commands.AddObject('Gamma', @Gamma);
end;

destructor TExample.Destroy;
begin
  Commands.Free;
  inherited Destroy;
end;

procedure TExample.ExecCommand(Cmd, Msg: string);
type
  TProcType = procedure(Msg: string);
var
  i: integer;
  P: TProcType;
begin
  i := Commands.IndexOf(Cmd);
  if i >= 0 then
  begin
    P := TProcType(Commands.Objects[i]);
    P(Msg);
  end;
end;

procedure TExample.Alpha(Msg: string);
begin
  ShowMessage('Alpha: ' + Msg);
end;

procedure TExample.Beta(Msg: string);
begin
  ShowMessage('Beta: ' + Msg);
end;

procedure TExample.Gamma(Msg: string);
begin
  ShowMessage('Gamma: ' + Msg);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Example: TExample;
  Cmd, Msg: string;
begin
  Cmd := Edit1.Text;
  Msg := Edit2.Text;
  Example := TExample.Create;
  Example.ExecCommand(Cmd, Msg);
  Example.Free;
end;

您试图对
TExample
对象调用非静态类方法,因此需要将
of object
添加到
TPropType
的声明中,以处理
Self
参数:

类型
TProcType=对象的过程(消息:字符串);
但是,非静态对象方法指针比普通指针大,因为它们携带两条信息—一条指向对象的指针,一条指向调用对象的方法的指针—因此不能直接将非静态方法指针存储在
TStringList.Objects[]
列表中。但是,您可以间接存储它

一种方法是动态分配方法指针,例如:

类型
TExample=class
公众的
var命令:TStringList;
构造函数创建;
毁灭者毁灭;推翻
过程执行命令(Cmd,Msg:string);
程序Alpha(Msg:string);
程序Beta(Msg:string);
程序伽马(消息:字符串);
结束;
类型
TProcType=对象的过程(消息:字符串);
PProcType=^TProcType;
构造函数TExample.Create;
变量
P:PProcType;
开始
继承创造;
命令:=TStringList.Create;
新的(P);
P^:=@Alpha;
AddObject('Alpha',TObject(P));
新的(P);
P^:=@Beta;
AddObject('Beta',TObject(P));
新的(P);
P^:=@伽马;
AddObject('Gamma',TObject(P));
结束;
析构函数TExample.Destroy;
变量
I:整数;
开始
对于I:=0到Commands.Count-1 do
Dispose(pprotype(Commands.Objects[I]);
命令。免费;
继承性破坏;
结束;
过程TExample.ExecCommand(Cmd,Msg:string);
变量
i:整数;
P:PProcType;
开始
i:=Commands.IndexOf(Cmd);
如果i>=0,则
开始
P:=pprotype(Commands.Objects[i]);
P^(味精);
结束;
结束;
过程TExample.Alpha(消息:字符串);
开始
ShowMessage('Alpha:'+Msg);
结束;
程序TExample.Beta(消息:字符串);
开始
ShowMessage('Beta:'+Msg);
结束;
程序TExample.Gamma(消息:字符串);
开始
ShowMessage('Gamma:'+Msg);
结束;
程序TForm1.按钮1单击(发送方:TObject);
变量
示例:TExample;
Cmd,Msg:string;
开始
Cmd:=Edit1.Text;
Msg:=Edit2.Text;
示例:=TExample.Create;
ExecCommand(Cmd,Msg);
例如,免费;
结束;
另一种方法是存储指向类方法的静态指针,然后在需要调用方法时使用记录来帮助您,如注释中描述的@OndrejKelle,例如:

类型
TExample=class
公众的
var命令:TStringList;
构造函数创建;
毁灭者毁灭;推翻
过程执行命令(Cmd,Msg:string);
程序Alpha(Msg:string);
程序Beta(Msg:string);
程序伽马(消息:字符串);
结束;
类型
TProcType=对象的过程(消息:字符串);
构造函数TExample.Create;
开始
继承创造;
命令:=TStringList.Create;
Commands.AddObject('Alpha',TObject(@TExample.Alpha));
Commands.AddObject('Beta',TObject(@TExample.Beta));
Commands.AddObject('Gamma',TObject(@TExample.Gamma));
结束;
析构函数TExample.Destroy;
开始
命令。免费;
继承性破坏;
结束;
过程TExample.ExecCommand(Cmd,Msg:string);
变量
i:整数;
P:TProcType;
开始
i:=Commands.IndexOf(Cmd);
如果i>=0,则
开始
t方法(P)。数据:=自身;
TMethod(P).Code:=指针(Commands.Objects[i]);
P(味精);
结束;
结束;
过程TExample.Alpha(消息:字符串);
开始
ShowMessage('Alpha:'+Msg);
结束;
程序TExample.Beta(消息:字符串);
开始
ShowMessage('Beta:'+Msg);
结束;
程序TExample.Gamma(消息:字符串);
开始
ShowMessage('Gamma:'+Msg);
结束;
程序TForm1.按钮1单击(发送方:TObject);
变量
示例:TExample;
Cmd,Msg:string;
开始
Cmd:=Edit1.Text;
Msg:=Edit2.Text;
示例:=TExample.Create;
ExecCommand(Cmd,Msg);
例如,免费;
结束;
但是不管怎样,一个
TStringList
都不是这个工作的最佳工具。你真的应该改用a,这样你就不必跳过不必要的圈,例如:

使用
…,System.Generics.Collections;
类型
TProcType=对象的过程(消息:字符串);
TExample=class
公众的
var命令:TDictionary;
构造函数创建;
毁灭者毁灭;推翻
过程执行命令(Cmd,Msg:string);
程序Alpha(Msg:string);
程序Beta(Msg:string);
程序伽马(消息:字符串);
结束;
构造函数TExample.Create;
开始
继承创造;
命令:=TDictionary.Create;
Commands.Add('Alpha',@Alpha);
添加('Beta',@Beta);
Add('Gamma',@Gamma);
结束;
析构函数TExample.Destroy;
开始
命令。免费;
继承性破坏;
结束;
过程TExample.ExecCommand(Cmd,Msg:string);
变量
P:TProcType;
开始
如果命令.TryGetValue(Cmd,P),则
P(味精);
结束;
过程TExample.Alpha(消息:字符串);
开始
ShowMessage('Alpha:'+Msg);
结束;
程序TExample.Beta(消息:字符串);
开始
ShowMessage('Beta:'+Msg);
结束;
程序TExample.Gamma(消息:字符串);
开始
ShowMessage('Gamma:'+Msg);
结束;
程序TForm1.按钮1单击(发送方:TObject);
变量
示例:TExample;
Cmd,Msg:string;
开始
Cmd:=Edit1.Text;
Msg:=Edit2.Text;
示例:=TExample.Create;
ExecCommand(Cmd,Msg);
例如,免费;
结束;

您试图对
TExample
对象调用非静态类方法,因此需要将
of object
添加到
TPropType
的声明中,以处理
Self
参数:

类型
TProcType=过程(消息:字符串)
type
  TExample = class
  published
    procedure Alpha(Msg: string);
    procedure Beta(Msg: string);
    procedure Gamma(Msg: string);
  public
    procedure ExecCommand(Cmd, Msg: string);
  end;

procedure TExample.ExecCommand(Cmd, Msg: string);
type
  TProcType = procedure(Msg: string) of object;
var
  M: TMethod;
  P: TProcType;
begin
  M.Code := Self.MethodAddress(Cmd);
  if M.Code = Nil then ShowMessage('Unknown command: ' + Cmd) else
  begin
    M.Data := Pointer(Self);
    P := TProcType(M);
    P(Msg);
  end;
end;