Does the TRttiMethod.Invoke function not work in overloaded methods?

I instantiate the class using the TRttiMethod.Invoke function, but when the constructor or method is overloaded, rtti does not call the correct method.

I wrote an example application to configure 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. 

Is this an RTTI error? or is there another way to call overloaded class methods using RTTI?

+5
rtti delphi delphi-xe2
source share
1 answer

There is nothing wrong with the TRttiMethod.Invoke method, your problem is in GetMethod . This function internally calls TRttiType.GetMethods and retrieves a pointer to the first method that matches the name passed as a parameter. Therefore, when you execute this t.GetMethod('Create') code, you always get a pointer to the same method.

To execute an overloaded version of a constructor or other method, you must resolve the method address to execute based on 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 the following 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]); 
+12
source share

All Articles