string Delphi:调用名称存储在字符串中的函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4186458/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 00:53:20  来源:igfitidea点击:

Delphi: Call a function whose name is stored in a string

stringdelphifunction

提问by Woutb21

Is it possible to call a function whose name is stored in a string in Delphi?

是否可以在 Delphi 中调用名称存储在字符串中的函数?

回答by himself

Please give more details on what are you trying to achieve.

请详细说明您要实现的目标。

As far as I know:

据我所知:

  • It is not possible to call a random function like that.
  • For class and object functions (MyObject.Function) this can be done with RTTI, but it's a lot of work.
  • If you just need to call one particular type of functions (say, function(integer, integer): string), it's a lot easier.
  • 不可能像那样调用随机函数。
  • 对于类和对象函数 (MyObject.Function),这可以通过 RTTI 来完成,但需要做很多工作。
  • 如果您只需要调用一种特定类型的函数(例如 function(integer, integer): string),那就容易多了。

For the last one, declare a function type, then get a function pointer and cast it like this:

对于最后一个,声明一个函数类型,然后获取一个函数指针并像这样转换它:

type
  TMyFuncType = function(a: integer; b: integer): string of object;

  TMyClass = class
  published
    function Func1(a: integer; b: integer): string;
    function Func2(a: integer; b: integer): string;
    function Func3(a: integer; b: integer): string;
  public
    function Call(MethodName: string; a, b: integer): string;
  end;

function TMyClass.Call(MethodName: string; a, b: integer): string;
var m: TMethod;
begin
  m.Code := Self.MethodAddress(MethodName); //find method code
  m.Data := pointer(Self); //store pointer to object instance
  Result := TMyFuncType(m)(a, b);
end;

{...}

//use it like this
var MyClass: TMyClass;
begin
  MyClass := TMyClass.Create;
  MyClass.Call('Func1', 3, 5);
  MyClass.Call('Func2', 6, 4);
  MyClass.Destroy;
end.

回答by Mohammed Nasman

You didn't specify your Delphi version, However if you have Delphi 2010(+) you can do it using the enhanced RTTI, I'm not expert on them, but I tried this sample for you:

你没有指定你的 Delphi 版本,但是如果你有 Delphi 2010(+) 你可以使用增强的 RTTI 来做,我不是他们的专家,但我为你尝试了这个示例:

  TProcClass = class
    public
      procedure SayHi;
      function GetSum(X,Y:Integer): Integer;
  end;

uses
  Rtti;

{ TProcClass }

procedure TProcClass.SayHi;
begin
  ShowMessage('Hi');
end;

function TProcClass.GetSum(X, Y: Integer): Integer;
begin
  ShowMessage(IntToStr(X + Y));
end;

procedure ExecMethod(MethodName:string; const Args: array of TValue);
var
 R : TRttiContext;
 T : TRttiType;
 M : TRttiMethod;
begin
  T := R.GetType(TProcClass);
  for M in t.GetMethods do
    if (m.Parent = t) and (m.Name = MethodName)then
      M.Invoke(TProcClass.Create,Args)
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ExecMethod('SayHi',[]);
  ExecMethod('GetSum',[10,20]);
end;

The good things, if you have procedure or function with parameters it will work without more work.

好东西,如果你有带参数的过程或函数,它会在没有更多工作的情况下工作。

回答by Kenneth Cochran

I'm surprised no one has suggested a dispatch table. This is exactly what it's for.

我很惊讶没有人建议使用调度表。这正是它的用途。

program RPS;

uses
  SysUtils,
  Generics.Collections;

type
  TDispatchTable = class(TDictionary<string, TProc>);

procedure Rock;
begin
end;

procedure Paper;
begin
end;

procedure Scissors;
begin
end;

var
  DispatchTable: TDispatchTable;

begin
  DispatchTable := TDispatchTable.Create;
  try
    DispatchTable.Add('Rock', Rock);
    DispatchTable.Add('Paper', Paper);
    DispatchTable.Add('Scissors', Scissors);

    DispatchTable['Rock'].Invoke; // or DispatchTable['Rock']();
  finally
    DispatchTable.Free;
  end;
end.

The implementation I wrote uses generics so it would only work with Delphi 2009+. For older versions it would probably be easiest to implement using TStringList and the command pattern

我编写的实现使用泛型,因此它只能与 Delphi 2009+ 一起使用。对于旧版本,使用 TStringList 和命令模式可能最容易实现

回答by Henri Gourvest

With Delphi 2010 you can uses JSON and SuperObject to invoke method with parametters.

使用 Delphi 2010,您可以使用 JSON 和 SuperObject 来调用带参数的方法。

http://code.google.com/p/superobject/source/browse/#svn/trunk

http://code.google.com/p/superobject/source/browse/#svn/trunk

If you need, there is also an xml parser to transform xml to json.

如果需要,还有一个 xml 解析器可以将 xml 转换为 json。

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure TestMethod(const value: string);
  end;

var
  Form1: TForm1;

implementation
uses superobject;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  SOInvoke(Self, 'TestMethod', SO('{value: "hello"}'));
end;

procedure TForm1.TestMethod(const value: string);
begin
  Caption := value;
end;

回答by AlexV

If you are asking if there is something like the JavaScript eval()is possible in Delphi, no this is not (easily) achievable since Delphi compiles to native code.

如果您问eval()Delphi 中是否可以使用 JavaScript 之类的东西,不,这不是(很容易)实现的,因为 Delphi 编译为本机代码。

If you need only to support some strings you can always do many ifor a case... Something like:

如果你只需要支持一些字符串,你总是可以做很多ifcase......类似的事情:

if myString = 'myFunction' then
    myFunction();

回答by Rob McDonell

Put each function in an Action. Then you can find the Action by name and Execute it

将每个函数放在一个 Action 中。然后你可以按名称找到Action并执行它

function ExecuteActionByName(const S: String);
var
  I: Integer;
begin
  for I := 0 to MainForm.ComponentCount-1 do
    if (MainForm.Components[I] is TAction)
    and SameText(TAction(MainForm.Components[I]).Name,S) then
    begin
      TAction(MainForm.Components[I]).Execute;
      Break;
    end;
end;

回答by TheDude

OK, I'm verylate to the party, but you can definitely call routines by name with this code (There are some limitations thought)

OK,我非常迟到了,但你绝对可以调用名称程序使用此代码(有一些限制以为)

type
    TExec = procedure of Object;
    // rest of section...

procedure TMainForm.ExecuteMethod(MethodName : String);
var
   Exec    : TExec;
   Routine : TMethod;
begin
     Routine.Data := Pointer(Form1);
     Routine.Code := Form1.MethodAddress(MethodName);
     if Not Assigned(Routine.Code) then
        Exit;

     Exec         := TExec(Routine);
     Exec;
end;

Just in case someone needs this for Delphi 7 / 2010

以防万一有人需要这个用于 Delphi 7 / 2010

回答by Larry Lustig

You can do something like this by crafting one or more classes with published properties that use functions to implement their read and write functionality. The properties can then be discovered using RTTI reflection and referenced, causing the underlying functions to get called.

您可以通过制作一个或多个具有已发布属性的类来执行类似的操作,这些属性使用函数来实现其读写功能。然后可以使用 RTTI 反射发现并引用这些属性,从而调用底层函数。

Alternatively, you can store function pointers in a table, or even the Object property of TStringList and effectively index them by string name.

或者,您可以将函数指针存储在表中,甚至是 TStringList 的 Object 属性中,并按字符串名称有效地索引它们。

Straight calling of a function by name is not possible in Delphi.

在 Delphi 中无法直接按名称调用函数。

回答by Daniel

The following simple solution using exportsand GetProcAddressalso works for old Delphi versions:

以下使用导出GetProcAddress 的简单解决方案也适用于旧的 Delphi 版本:

type
    TMyProc = procedure(const value: Integer);

    procedure Test(const value: Integer);

    exports Test;

implementation

procedure Test(const value: string);
begin
    ShowMessage('It works! '  + value);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
    p: TMyProc;
begin
    p := GetProcAddress(HInstance, 'Test'); 
    if Assigned(p) then P('Yes');
end;

回答by MrClarc

function ExecuteMethod(AClass : TClass; AMethodName : String; const AArgs: Array of TValue) : TValue;
var
  RttiContext : TRttiContext;
  RttiMethod  : TRttiMethod;
  RttiType    : TRttiType;
  RttiObject  : TObject;
begin
  RttiObject := AClass.Create;
  try
    RttiContext := TRttiContext.Create;
    RttiType    := RttiContext.GetType(AClass);
    RttiMethod  := RttiType.GetMethod(AMethodName);
    Result      := RttiMethod.Invoke(RttiObject,AArgs);
  finally
    RttiObject.Free;
  end;
end;