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.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;
以下 TRttiRecordField<T>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.
コメント