将对象方法添加到字符串列表,以便可以按名称调用它们 [英] Adding object methods to a stringlist so they can be invoked by name
问题描述
我拥有的服务器代码比接受来自客户端的命令并执行对象方法(由收到的命令确定)更重要。我想使用AddObject构建一个字符串列表,以将命令与所需过程相关联。这在独立过程中可以正常工作,但是在尝试将对象方法添加到字符串列表时出现需要变量错误。下面是示例代码:
I have server code than accepts commands from clients and executes object methods, as determined by the command received. I want to build a stringlist using AddObject to associate the command with the desired procedure. This works fine with standalone procedures but I get "variable required" errors when trying to add object methods to my stringlist. Here's example code:
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
对象,因此需要将对象
添加到 TPropType $的声明中c $ c>来处理
Self
参数:
You are trying to call non-static class methods on TExample
objects, so you need to add of object
to the declaration of TPropType
to handle the Self
parameter:
type
TProcType = procedure(Msg: string) of object;
但是,非静态对象方法的指针要比普通的香草指针大,因为它们带有两个信息-指向对象的指针,以及指向要在对象上调用的方法的指针-因此,您不能直接将非静态方法指针存储在 TStringList中。对象[]
列表。但是,您可以将其间接存储。
However, non-static object method pointers are larger then plain vanilla pointers, as they carry two pieces of information - a pointer to the object, and a pointer to the method to call on the object - so you can't directly store a non-static method pointer in the TStringList.Objects[]
list. However, you can store it indirectly.
一种方法是动态分配方法指针,例如:
One way is to dynamically allocating the method pointers, eg:
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;
type
TProcType = procedure(Msg: string) of object;
PProcType = ^TProcType;
constructor TExample.Create;
var
P: PProcType;
begin
inherited Create;
Commands := TStringList.Create;
New(P);
P^ := @Alpha;
Commands.AddObject('Alpha', TObject(P));
New(P);
P^ := @Beta;
Commands.AddObject('Beta', TObject(P));
New(P);
P^ := @Gamma;
Commands.AddObject('Gamma', TObject(P));
end;
destructor TExample.Destroy;
var
I: Integer;
begin
for I := 0 to Commands.Count-1 do
Dispose(PProcType(Commands.Objects[I]));
Commands.Free;
inherited Destroy;
end;
procedure TExample.ExecCommand(Cmd, Msg: string);
var
i: integer;
P: PProcType;
begin
i := Commands.IndexOf(Cmd);
if i >= 0 then
begin
P := PProcType(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;
另一种方法是存储指向类方法的静态指针,然后使用 TMethod
记录可以在以下情况下为您提供帮助您需要调用方法,例如注释中描述的@OndrejKelle,例如:
Another way is to store static pointers to the class methods, and then make use of the TMethod
record to help you when you need to call the methods, like @OndrejKelle described in comments, eg:
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;
type
TProcType = procedure(Msg: string) of object;
constructor TExample.Create;
begin
inherited Create;
Commands := TStringList.Create;
Commands.AddObject('Alpha', TObject(@TExample.Alpha));
Commands.AddObject('Beta', TObject(@TExample.Beta));
Commands.AddObject('Gamma', TObject(@TExample.Gamma));
end;
destructor TExample.Destroy;
begin
Commands.Free;
inherited Destroy;
end;
procedure TExample.ExecCommand(Cmd, Msg: string);
var
i: integer;
P: TProcType;
begin
i := Commands.IndexOf(Cmd);
if i >= 0 then
begin
TMethod(P).Data := Self;
TMethod(P).Code := Pointer(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;
但是无论哪种方式, TStringList
的确是不是这项工作的最佳工具。您确实应该使用 TDictionary $ c
,那么您就不必跳过不必要的箍,例如:
But either way, a TStringList
is really not the best tool for this job. You really should use a TDictionary
instead, then you are not having to jump through unnecessary hoops, eg:
uses
..., System.Generics.Collections;
type
TProcType = procedure(Msg: string) of object;
TExample = class
public
var Commands: TDictionary<String, TProcType>;
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 := TDictionary<String, TProcType>.Create;
Commands.Add('Alpha', @Alpha);
Commands.Add('Beta', @Beta);
Commands.Add('Gamma', @Gamma);
end;
destructor TExample.Destroy;
begin
Commands.Free;
inherited Destroy;
end;
procedure TExample.ExecCommand(Cmd, Msg: string);
var
P: TProcType;
begin
if Commands.TryGetValue(Cmd, P) then
P(Msg);
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;
这篇关于将对象方法添加到字符串列表,以便可以按名称调用它们的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!