2012-04-10 23 views
5

TRttiMethod.Invoke işlevini kullanarak sınıfın bir örneğini oluşturuyorum, ancak yapıcı veya yöntem aşırı yüklendiğinde, rtti uygun yöntemi çağırmaz.TRttiMethod.Invoke işlevi aşırı yüklenmiş yöntemlerde çalışmıyor mu?

Sorunumu temizlemek için örnek bir uygulama yazdım.

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. 

Bu bir RTTI hatası mı? veya RTTI'yi kullanarak bir sınıfın aşırı yüklenmiş yöntemlerini çağırmanın başka bir yolu var mı?

cevap

12

TRttiMethod.Invoke yöntemiyle ilgili bir sorun yok, sorununuzun adresi GetMethod. Bu işlev dahili olarak TRttiType.GetMethods'a çağrı yapar ve parametre olarak iletilen adla eşleşen birinci yönteme bir işaretçi getirir. Yani bu kodu t.GetMethod('Create') yürütürken her zaman aynı yönteme bir işaretçi oluyorsunuz.

Kurucunun aşırı yüklenmiş bir sürümünü veya başka bir yöntemi yürütmek için, parametrelere göre yürütmek üzere yöntem adresini çözmeniz ve ardından TRttiMethod.Invoke işlevini çağırmanız gerekir.

Bu örnek işlevi kontrol edin.

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; 

Artık bu yollardan birini sınıfınızın kurucular veya yöntemlerini çağırabilir

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]); 
İlgili konular