【问题标题】:TRttiMethod.Invoke function doesn't work in overloaded methods?TRttiMethod.Invoke 函数在重载方法中不起作用?
【发布时间】:2012-04-10 05:20:38
【问题描述】:

我正在使用TRttiMethod.Invoke 函数创建一个类的实例,但是当构造函数或方法被重载时,rtti 不会调用正确的方法。

我编写了一个示例应用程序来说明我的问题。

program ProjectFoo;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Rtti,
  System.SysUtils;

type
  TFoo=class
  public
    constructor Create(Value :  Integer);overload;
    constructor Create(const Value :  string);overload;
    function Bar(value : integer) : Integer; overload;
    function Bar(const value : string) : string; overload;
  end;

{ TFoo }

constructor TFoo.Create(Value: Integer);
begin
   Writeln(Value);
end;

function TFoo.Bar(value: integer): Integer;
begin
   Writeln(Value);
   Result:=value;
end;

function TFoo.Bar(const value: string): string;
begin
   Writeln(Value);
   Result:=value;
end;


constructor TFoo.Create(const Value: string);
begin
   Writeln(Value);
end;

var
 c : TRttiContext;
 t : TRttiInstanceType;
 r : TValue;
begin
  try
   c := TRttiContext.Create;
   t := (c.GetType(TFoo) as TRttiInstanceType);
   r := t.GetMethod('Create').Invoke(t.MetaclassType,[444]);//this works 
   //r := t.GetMethod('Create').Invoke(t.MetaclassType,['hello from constructor string']);//this fails : EInvalidCast: Invalid class typecast
   t.GetMethod('Bar').Invoke(r,[1]);// this works
   //t.GetMethod('Bar').Invoke(r,['Hello from bar']); //this fails : EInvalidCast: Invalid class typecast
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
   readln;
end.

这是一个 RTTI 错误?还是存在另一种使用 RTTI 调用类的重载方法的方法?

【问题讨论】:

    标签: delphi delphi-xe2 rtti


    【解决方案1】:

    TRttiMethod.Invoke 方法没有问题,您的问题位于GetMethod。此函数在内部调用TRttiType.GetMethods 并检索指向与作为参数传递的名称匹配的第一个方法的指针。因此,当您执行此代码 t.GetMethod('Create') 时,您总是会获得指向同一方法的指针。

    要执行构造函数的重载版本或其他方法,您必须根据参数解析要执行的方法地址,然后调用TRttiMethod.Invoke函数。

    检查这个示例函数。

    function RttiMethodInvokeEx(const MethodName:string; RttiType : TRttiType; Instance: TValue; const Args: array of TValue): TValue;
    var
     Found   : Boolean;
     LMethod : TRttiMethod;
     LIndex  : Integer;
     LParams : TArray<TRttiParameter>;
    begin
      Result:=nil;
      LMethod:=nil;
      Found:=False;
      for LMethod in RttiType.GetMethods do
       if SameText(LMethod.Name, MethodName) then
       begin
         LParams:=LMethod.GetParameters;
         if Length(Args)=Length(LParams) then
         begin
           Found:=True;
           for LIndex:=0 to Length(LParams)-1 do
           if LParams[LIndex].ParamType.Handle<>Args[LIndex].TypeInfo then
           begin
             Found:=False;
             Break;
           end;
         end;
    
         if Found then Break;
       end;
    
       if (LMethod<>nil) and Found then
         Result:=LMethod.Invoke(Instance, Args)
       else
         raise Exception.CreateFmt('method %s not found',[MethodName]);
    end;
    

    现在您可以通过以下方式之一调用类的构造函数或方法

       r := RttiMethodInvokeEx('Create', t, t.MetaclassType, [444]);
       r := RttiMethodInvokeEx('Create', t, t.MetaclassType, ['hello from constructor string']);
       r := RttiMethodInvokeEx('Create', t, t.MetaclassType, []);
       RttiMethodInvokeEx('Bar', t, r.AsObject , ['this is a string']);
       RttiMethodInvokeEx('Bar', t, r.AsObject , [9999]);
    

    【讨论】:

    • 这在你传入后代类时不起作用。例如,如果我有一个方法 Add(MyObject: TObject) 并传入一个 TComponent,将找不到具有正确参数的方法。对于具有Args[LIndex].AsObject.InheritsFrom(LParams[LIndex].ParamType.AsInstance.MetaclassType) 之类的对象,您还需要检查继承。
    猜你喜欢
    • 2017-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-11
    • 1970-01-01
    • 2013-08-28
    • 1970-01-01
    相关资源
    最近更新 更多