将对象方法添加到字符串列表,以便可以按名称调用它们 [英] Adding object methods to a stringlist so they can be invoked by name

查看:90
本文介绍了将对象方法添加到字符串列表,以便可以按名称调用它们的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我拥有的服务器代码比接受来自客户端的命令并执行对象方法(由收到的命令确定)更重要。我想使用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 来处理 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 ,那么您就不必跳过不必要的箍,例如:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆