0

TObjectList<T>RTTI を使用して任意の Delphi オブジェクトをストリーミングするための汎用コードを実装しています。これを機能させるには (より具体的には、読み込み部分を機能させるために)、フィールドのサブアイテム タイプを取得する必要があります。実際のオブジェクト インスタンスを使用せずに。

実際のオブジェクト インスタンスを使用しないという要件の明白な理由は、ストリームからオブジェクトをロードする場合 (ロードされるオブジェクトのクラス タイプの知識のみに基づいて)、インスタンスがないことです。ロードが完了する前にすべて利用可能です。むしろ、問題のクラスの純粋な RTTI データにのみアクセスできます。

ロードできるようにしたいクラスの例は次のとおりです。

TTestClass = class(TObject)
public
   test_list : TList<string>;
end;

私が望むのは、test_listフィールドが一般的なTList<T>場所であると結論付けられることTですstring(つまり、サブアイテムのストリームから期待されるデータを知るため)。

クラスが代わりに次のようになった場合:

TTestClassWithArr = class(TObject)
public
   test_arr : array of string;
end;

フィールドのRTTI クラスのElementType()メソッドを使用して、純粋に RTTI を介してこの情報を抽出できますが、対応するそのような明示的な RTTI タイプが見つかりません。TRttiDynamicArrayTypetest_arrTObjectList<T>

別のスタック オーバーフローの質問 ( Delphi Rtti: how to get objects fromTObjectList<T> ) は関連していますが、実際には、RTTI データが反映するオブジェクトの実際のインスタンスを使用して、サブアイテムに到達するために「チート」します。これらのサブアイテムは、私がこれを知っている必要がある時点では存在しないため、私にはオプションではありません.

ただし、オブジェクトのインスタンス化に関係なく、コンパイル時にすべての型情報が明らかに存在するため、クラスの RTTI 情報のみを使用してこれを行う方法があるはずです。

4

2 に答える 2

3

残念ながら、ジェネリック パラメータに対して生成される RTTI はありません。TGeneric コンテナ内の の値を検出する唯一の方法は、ターゲット フィールド自体の を取得し、そのメソッドを呼び出してTList<T>そのクラス名を文字列として取得し、括弧内の部分文字列を解析することです。例えば:TRttiTypeToString()

uses
  ..., System.StrUtils, System.Rtti;

var
  Ctx: TRttiContext;
  s: string;
  OpenBracket, CloseBracket: Integer;
  ...
begin
  ...
  s := Ctx.GetType(TTestClass).GetField('test_list').FieldType.ToString; // returns 'TList<System.string>'
  OpenBracket := Pos('<', s);
  CloseBracket := PosEx('>', s, OpenBracket+1);
  s := Copy(s, OpenBracket+1, CloseBracket-OpenBracket-1); // returns 'System.string'
  // if multiple Generic parameters are present, they will be separated by commas...
  ...
end;

Generic パラメータを文字列として抽出したらTRttiContext.FindType()、そのタイプの RTTI にアクセスする必要がある場合に使用できます。

そうは言っても、次のコードは一連の RTTI ヘルパーを提供します。

DSharp.Core.Reflection.pas (Google コード)

DSharp.Core.Reflection.pas (BitBucket)

とりわけ、メソッドを にTRttiTypeHelper追加するクラス ヘルパーを定義します。GetGenericArguments()TRttiType

TRttiTypeHelper = class helper for TRttiType
...
public 
  ...
  function GetGenericArguments: TArray<TRttiType>;
  ...
end;

内部的にGetGenericArguments()は、上記と同じ手法を使用します。それを使用すると、代わりにこれを行うことができます:

uses
  ..., System.Rtti, DSharp.Core.Reflection;

var
  Ctx: TRttiContext;
  arr: TArray<TRttiType>;
  typ: TRttiType;
  s: string;
  ...
begin
  ...
  arr := Ctx.GetType(TTestClass).GetField('test_list').FieldType.GetGenericArguments;
  typ := arr[0]; // returns RTTI for System.String
  s := typ.ToString; // returns 'System.string'
  ...
end;
于 2015-03-08T02:56:33.987 に答える