Delphi の RTTI で record の情報取得(2/2)
Delphi の RTTI(Delphi 2010以降) とジェネリックスを利用して record 型の情報取得
昨日のコードは、取得するだけで書き込む事を考えてなかったので、その辺りを変更
Create時の引数からポインタを保持して、record そのものを変更しに行く
これで、record の型と値を放り込んだら、レジストリやIniFileやSQLへの書き込みや、読み込みが可能になるクラスが作れます
独自のクラスを保存したい場合、FieldType の TypeKind へ tkClass が入るので、
if (Value.AsObject is TMyClass) then のようにクラスを判定して処理したり、さらに再帰して中身を分離したりできそう
ところで、ジェネリックスで与えられた型を使って、汎用ポインタからキャストって出来ないんですかね
みたいなのは OK ですが
みたいなのは、NGで一方通行なんです
昨日と似たコード(Value がプロパティに変更され、引数のカッコを変更)で、書き込みをしている
以下 TRttiRecordField<T>
昨日のコードは、取得するだけで書き込む事を考えてなかったので、その辺りを変更
Create時の引数からポインタを保持して、record そのものを変更しに行く
これで、record の型と値を放り込んだら、レジストリやIniFileやSQLへの書き込みや、読み込みが可能になるクラスが作れます
独自のクラスを保存したい場合、FieldType の TypeKind へ tkClass が入るので、
if (Value.AsObject is TMyClass) then のようにクラスを判定して処理したり、さらに再帰して中身を分離したりできそう
ところで、ジェネリックスで与えられた型を使って、汎用ポインタからキャストって出来ないんですかね
- var D: T;
- P: ^T;
- begin
- D := P^;
- end;
- var D: T;
- P: Pointer;
- begin
- P := @D;
- D := T(P); // NG
- D := (P as T); // NG
- D := <T>(P); // NG
- end;
昨日と似たコード(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<ttestrecord>.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;
- </ttestrecord>
- 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.
コメント