2012-04-10 8 views
5

Estoy creando una instancia de una clase usando la función TRttiMethod.Invoke, pero cuando el constructor o un método está sobrecargado, el rtti no llama al método apropiado.La función TRttiMethod.Invoke no funciona en métodos sobrecargados?

Escribí una aplicación de muestra para resolver mi problema.

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. 

¿Este es un error RTTI? ¿O existe otra forma de llamar a los métodos sobrecargados de una clase usando RTTI?

Respuesta

12

No hay nada de malo en el método TRttiMethod.Invoke, su problema se encuentra en el GetMethod. Esta función llama internamente al TRttiType.GetMethods y recupera un puntero al primer método que coincide con el nombre pasado como parámetro. Por lo tanto, cuando está ejecutando este código t.GetMethod('Create') siempre obtiene un puntero al mismo método.

Para ejecutar una versión sobrecargada del constructor u otro método, debe resolver la dirección del método para ejecutarla en los parámetros y luego llamar a la función TRttiMethod.Invoke.

Compruebe esta función de muestra.

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; 

Ahora se puede llamar a los constructores o métodos de su clase en una de estas formas

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