2012-04-10 33 views
5

我使用TRttiMethod.Invoke函數創建類的實例,但是當構造函數或方法重載時,rtti不會調用正確的方法。TRttiMethod.Invoke函數在重載方法中不起作用?

我寫了一個示例應用程序來解釋我的問題。

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調用類的重載方法?

回答

12

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