2011-10-21 4 views
4

RTTI(D2010バージョン14.0.3593.25826を実行中)で取得したコンストラクタを呼び出そうとしています。コンストラクタは文字列とオブジェクトの引数を引数として取ります。これらの引数はすべて''またはnilに初期化する必要があります。 (免責事項:私は希望のコンストラクタは、パラメータの最大数、したがって、奇妙に見える、けれども次善のデザインのいずれかになりますことを知っている)、次のようにDelphi:invokeコンストラクタがEInvalidCastを生成します

コードが行く:

program sb_rtti; 
{$APPTYPE CONSOLE} 
uses RTTI, TypInfo, SysUtils; 

type 

TMyClass = class (TObject) 
    FField1: string; 
    FObject1: TObject; 
public 
    constructor Create(Field1: string = ''; Object1: TObject = nil); 
end; 

constructor TMyClass.Create(Field1: string; Object1: TObject); 
begin 
    FField1 := Field1; 
    FObject1 := Object1; 
end; 

function GetConstructor(rType: TRttiType) : TRttiMethod; 
var 
    MaxParams: integer; 
    Methods: TArray<TRttiMethod>; 
    Method:  TRttiMethod; 
    Params:  TArray<TRttiParameter>; 
begin 
    Methods := rType.GetMethods('Create'); 
    MaxParams := 0; 
    for Method in Methods do begin 
    Params := Method.GetParameters(); 
    if (Length(Params) > MaxParams) then begin 
     Result := Method; 
     MaxParams := Length(Params); 
    end; 
    end; 
end; 

procedure InitializeParam(Param: TRttiParameter; ActualParam: TValue); 
begin 
    if (Param.ParamType.TypeKind = TTypeKind.tkClass) then begin 
    ActualParam := TValue.From<TObject>(nil); 
    end else if (Param.ParamType.TypeKind = TTypeKind.tkString) then begin 
    ActualParam := TValue.From<string>(''); 
    end else if (Param.ParamType.TypeKind = TTypeKind.tkUString) then begin 
    ActualParam := TValue.From<UnicodeString>(''); 
    end else begin 
    // Other types goes here 
    end; 
end; 

var 
    Context:  TRttiContext; 
    Constr:  TRttiMethod; 
    Params:  TArray<TRttiParameter>; 
    ResultValue: TValue; 
    rType:  TRttiType; 
    ActualParams: array of TValue; 
    i:   integer; 
    CurrentParam: TRttiParameter; 
begin 
    Context := TRttiContext.Create(); 
    rType := Context.GetType(TypeInfo(TMyClass)); 
    Constr := GetConstructor(rType); 
    try 
    if (Constr <> nil) then begin 
     Params := Constr.GetParameters(); 
     SetLength(ActualParams, Length(Params)); 
     for i := 0 to Length(Params) - 1 do begin 
     CurrentParam := Params[i] as TRttiParameter; 
     InitializeParam(CurrentParam, ActualParams[i]); 
     end; 
     ResultValue := Constr.Invoke(rType.AsInstance.MetaclassType, ActualParams); 
    end; 
    except 
    on E : Exception do 
     WriteLn(E.ToString); 
    end; 
    ReadLn; 
end. 

を今、とき行ResultValue := Constr.Invoke(rType.AsInstance.MetaclassType, ActualParams);が実行され、EInvalidCast例外が発生します。例外は、(argList[currArg] := Args[i].Cast(parList[i].ParamType.Handle);)rtti.pasにライン4093でより正確に、しかし、問題の肉は、コールスタック内の前の時点で見出されると思われる線1336

TValue.Cast -methodに追跡することができます。

私は、私が想定していない方法でrttiを使用していますが、どこでも記述された「正しい方法」を見つけることはできません。誰かが私を正しい方向に向けることができますか?ありがとう!

答えて

5

ActualParamパラメータの割り当てでは、あなたがそのパラメータのローカルコピーの値を設定しているので、あなたはInitializeParam手順で問題を抱えている - TValueActualParamのタイプ)がレコードであることを覚えておいてください。したがって、問題を解決するには、ActualParamをvarパラメータとして渡す必要があります。

procedure InitializeParam(Param: TRttiParameter; var ActualParam: TValue); 
+0

ありがとうございました、私はちょうど私に起きました...私は参照渡しと値渡しのルールを混ぜています。 :)私は、私の心は、 "それはrttiに関連していなければならなかった"というアイデアにとても固定されていると思うので、私は基本を忘れてしまった。 – conciliator

0

それだけで問題を解決し

ActualParams[0] := TValue.From<string>(''); 
ActualParams[1] := TValue.From<TObject>(nil); 

for i := 0 to Length(Params) - 1 do begin 
    CurrentParam := Params[i] as TRttiParameter; 
    InitializeParam(CurrentParam, ActualParams[i]); 
end; 

を置き換えることによって、ハードコード引数の初期化に私に起こりました。

関連する問題