TRttiMethod.Invoke function doesn't work in overloaded methods?

空扰寡人 提交于 2020-01-20 05:05:15

问题


I'm creating an instance of a class using the TRttiMethod.Invoke function , but when the constructor or a method is overloaded, the rtti does not call the proper method.

I wrote a sample app to ilustate my problem.

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.

This is a RTTI bug? or exist another way to call the overloaded methods of a class using the RTTI?


回答1:


There is nothing wrong with the TRttiMethod.Invoke method, your issue is located in the GetMethod. This function internally call to the TRttiType.GetMethods and retrieves a pointer to the first method which match with the name passed as parameter. So when you are executing this code t.GetMethod('Create') you always are getting a pointer to the same method.

To execute an overloaded version of the constructor or another method you must resolve the method address to execute based in the parameters, and then call the TRttiMethod.Invoke function.

Check this sample function.

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;

Now you can call the constructors or methods of your class in one of these ways

   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]);


来源:https://stackoverflow.com/questions/10083448/trttimethod-invoke-function-doesnt-work-in-overloaded-methods

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!