Delphi の RTTI で record の情報取得(2/2)
Delphi の RTTI(Delphi 2010以降) とジェネリックスを利用して record 型の情報取得
昨日のコードは、取得するだけで書き込む事を考えてなかったので、その辺りを変更
Create時の引数からポインタを保持して、record そのものを変更しに行く
これで、record の型と値を放り込んだら、レジストリやIniFileやSQLへの書き込みや、読み込みが可能になるクラスが作れます
独自のクラスを保存したい場合、FieldType の TypeKind へ tkClass が入るので、
if (Value.AsObject is TMyClass) then のようにクラスを判定して処理したり、さらに再帰して中身を分離したりできそう
ところで、ジェネリックスで与えられた型を使って、汎用ポインタからキャストって出来ないんですかね
昨日と似たコード(Value がプロパティに変更され、引数のカッコを変更)で、書き込みをしている
昨日のコードは、取得するだけで書き込む事を考えてなかったので、その辺りを変更
Create時の引数からポインタを保持して、record そのものを変更しに行く
これで、record の型と値を放り込んだら、レジストリやIniFileやSQLへの書き込みや、読み込みが可能になるクラスが作れます
独自のクラスを保存したい場合、FieldType の TypeKind へ tkClass が入るので、
if (Value.AsObject is TMyClass) then のようにクラスを判定して処理したり、さらに再帰して中身を分離したりできそう
ところで、ジェネリックスで与えられた型を使って、汎用ポインタからキャストって出来ないんですかね
var D: T; P: ^T; begin D := P^; end;みたいなのは OK ですが
var D: T; P: Pointer; begin P := @D; D := T(P); // NG D := (P as T); // NG D := <T>(P); // NG end;みたいなのは、NGで一方通行なんです
昨日と似たコード(Value がプロパティに変更され、引数のカッコを変更)で、書き込みをしている
procedure TForm1.Button1Click(Sender: TObject); var I: Integer; RecField: TRttiRecordField<TTestRecord>; TestRecord: TTestRecord; S, ValueStr: string; begin TestRecord.Key := 'Hello'; TestRecord.Value := 99; RecField := TRttiRecordField以下 TRttiRecordField<T>.Create(TestRecord); try for I := RecField.Count - 1 downto 0 do begin S := RecField.Name[I]; case RecField.ValueType(I) of tkInteger: ValueStr := IntToStr(RecField.Value[I].AsInteger); tkUString: ValueStr := RecField.Value[I].AsString; else ValueStr := '[else]'; end; if (S = 'Key') then begin RecField.Value[I] := 'Goodbye'; end; Memo1.Lines.Add(S + ' = ' + ValueStr); end; finally RecField.Free; end; Memo1.Lines.Add(TestRecord.Key); end;
unit RttiRecordFieldUnit; interface uses Classes, RTTI, TypInfo; type TRttiRecordField<T> = class private FRecData: T; PRecData: Pointer; FCtx: TRttiContext; FFields: TArray<TRttiField>; function GetValue(I: Integer): TValue; procedure SetValue(I: Integer; AValue: TValue); function GetName(I: Integer): string; public constructor Create(var ARecData: T); destructor Destroy; override; function Count: Integer; function ValueType(I: Integer): TTypeKind; property Name[I: Integer]: string read GetName; property Value[I: Integer]: TValue read GetValue write SetValue; end; implementation { TRttiRecordField<T> } constructor TRttiRecordField<T>.Create(var ARecData: T); begin PRecData := @ARecData; // FRecData := ARecData; FCtx := TRttiContext.Create; FFields := FCtx.GetType(TRttiRecordField<T>).GetField('FRecData').FieldType.GetFields; end; destructor TRttiRecordField<T>.Destroy; begin inherited; FCtx.Free; end; function TRttiRecordField<T>.Count: Integer; begin Result := Length(FFields); end; function TRttiRecordField<T>.GetName(I: Integer): string; begin Result := FFields[I].name; end; function TRttiRecordField<T>.ValueType(I: Integer): TTypeKind; begin Result := FFields[I].FieldType.TypeKind; end; function TRttiRecordField<T>.GetValue(I: Integer): TValue; begin Result := FFields[I].GetValue(PRecData); end; procedure TRttiRecordField<T>.SetValue(I: Integer; AValue: TValue); begin FFields[I].SetValue(PRecData, AValue); end; end.
コメント