Delphi の RTTI で record の情報取得(1/2)

Delphi 2010だったかで、機能追加された RTTI。 クラスなどのフィールド情報などを取得できる
( 2/2はこちら )

喜んで試してみたんだけど、class だけで record の情報は取得できない様子(できるのかもしれんが)
ただ、class メンバの record 型の情報は取れるので、ジェネリックスと絡めて record 型のメンバとデータを取得するクラスを作成してみた(後半のコード)

何がしたかったかというと、データ設定値などを record 型で作成して、レジストリやIniファイルへ保存するようなパターンで、フィールドが増えたときにいちいち保存や読込の所を修正しに行くのが面倒
TInifile を継承して、レコード型を渡すだけで、全て指定セクションに書き込んでちょ、みたいなのを最終的に利用できればよいと思う(SQLとのやりとりなんかにも)

Form1 に Button1 と Memo1 を貼って、TTestRecord のメンバと値を列挙する
(とりあえず String と Integer の例)

TRttiRecordField の Create で、型と実際の値を渡してやれば、あとはメソッドを呼び出すだけです
type
    TTestRecord = record
        Key: string;
        Value: Integer;
    end;

procedure TForm1.Button1Click(Sender: TObject);
var I: Integer;
    S: string;
    RecField: TRttiRecordField<TTestRecord>;
    TestRecord: TTestRecord;
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:
                    S := S + IntToStr(RecField.Value(I).AsInteger);
                tkUString:
                    S := S + RecField.Value(I).AsString;
            end;

            Memo1.Lines.Add(S);
        end;
    finally
        RecField.Free;
    end;
end;
以下 TRttiRecordField クラス (書き込みに対応したコードは、こちら)
unit RttiRecordFieldUnit;
interface

uses Classes, RTTI, TypInfo;

type
    TRttiRecordField<T> = class
    private
        FRecordData: T;
        FCtx: TRttiContext;
        FFields: TArray<TRttiField>;
    public
        constructor Create(ARecordData: T);
        destructor Destroy; override;

        function Count: Integer;
        function ValueType(I: Integer): TTypeKind;
        function Name(I: Integer): string;
        function Value(I: Integer): TValue;
    end;

implementation

{ TRttiRecordField<T> }

constructor TRttiRecordField<T>.Create(ARecordData: T);
begin
    FRecordData := ARecordData;
    FCtx := TRttiContext.Create;
    FFields := FCtx.GetType(TRttiRecordField<T>).GetField('FRecordData').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>.Name(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>.Value(I: Integer): TValue;
begin
    Result := FFields[I].GetValue(@FRecordData);
end;

end.

コメント