1
    type
      TMyRecord = record
      private
        class constructor create;
      public
        class var MyField1: string;                
        class var MyField2: integer;            
        class var MyField3: extended;             
        class function ToString: string; static;
      end;

    class constructor TMyRecord.Create;
    begin
        TMyRecord.MyField1 := 'Hello, world!';
        TMyRecord.MyField2 := 123;
        TMyRecord.MyField3 := 3.1415927;
    end;  

    class function TMyRecord.ToString: string;
    var
      RecType: TRTTIType;
      RecFields: TArray<TRttiField>;
      I: integer;
    begin
      RecType := TRTTIContext.Create.GetType(TypeInfo(TMyRecord));
      Result := RecType.ToString;
      RecFields := RecType.GetFields;
      for I := 0 to High(RecFields) do
        Result := Result + Format('%s: %s = %s', [RecFields[I].Name, RecFields[I].FieldType.ToString, RecFields[I].GetValue(@TMyRecord).ToString]) + sLineBreak;
    end;

我试图让 TMyRecord.ToString 返回:

    TMyRecord
    MyField1: string = Hello, world!
    MyField2: integer = 123;
    MyField3: extended = 3.1415927;

但是,我在 GetValue(@TMyRecord) 上收到编译器错误 - E2029 '(' expected but ')' found

通常应使用记录的“实例”地址调用 GetValue。但在这种情况下,记录是静态的。我不想将此记录转换为普通记录、创建实例等。我正在尝试为静态记录解决此问题。如何获取应该传递给 GetValue 的地址?

4

3 回答 3

3

据我所知,类字段无法通过 RTTI 访问。

于 2017-03-29T09:16:44.887 回答
2

正如在另一个答案中所说,无法使用 RTTI 访问类 var 字段。

但是通过一个小的调整,仍然可以获得想要的解决方案:

program TestClassVar;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,System.RTTI;

type
  TMyRecord = record
    type
      TIR = record
        MyField1: string;
        MyField2: integer;
        MyField3: extended;
      end;
  private
    class constructor create;
  public
    class var r: TIR;
    class function ToString: string; static;
  end;

class constructor TMyRecord.Create;
begin
  TMyRecord.r.MyField1 := 'Hello, world!';
  TMyRecord.r.MyField2 := 123;
  TMyRecord.r.MyField3 := 3.1415927;
end;

class function TMyRecord.ToString: string;
var
  RecType: TRTTIType;
  RecFields: TArray<TRttiField>;
  I: integer;
  firstFieldAdr: Pointer;
begin
  RecType := TRTTIContext.Create.GetType(TypeInfo(TMyRecord));
  Result := RecType.ToString + sLineBreak;
  RecType := TRTTIContext.Create.GetType(TypeInfo(TIR));
  RecFields := RecType.GetFields;

  firstFieldAdr := Addr(TMyRecord.r);
  for I := 0 to High(RecFields) do
    Result :=
      Result +
      Format('%s: %s = %s',
        [RecFields[I].Name,
        RecFields[I].FieldType.ToString,
        RecFields[I].GetValue(firstFieldAdr).ToString])
        +
      sLineBreak;    
end;

begin
  WriteLn(TMyRecord.ToString);
  ReadLn;
end.

这些字段被放入内部记录,第一个字段的地址通过Addr()函数解析。

缺点是对字段的访问将有一个额外的r.说明符。


实际输出:

TMyRecord
MyField1: string = Hello, world!
MyField2: Integer = 123
MyField3: Extended = 3.1415927 
于 2017-03-29T12:29:51.030 回答
0

感谢陆路的贡献!非常聪明,使用嵌套记录并声明本地类 var 'r',允许访问类型和实例。

同时我想出了另一个解决方案,虽然它使用一个类(带有外部实例)而不是一个记录,我不想这样做,但我没有看到另一种方式,所以谢谢你解决方案!

type
  TMyRecord = class
  private
    constructor create;
  public
    MyField1: string;                
    MyField2: integer;            
    MyField3: extended;             
    function ToString: string; 
  end;
var
  MyRecord: TMyRecord;

constructor TMyRecord.Create;
begin
  MyField1 := 'Hello, world!';
  MyField2 := 123;
  MyField3 := 3.1415927;
end;  

function TMyRecord.ToString: string;
var
  Field: TRttiField;
begin
  Result := sLineBreak;
  for Field in TRTTIContext.Create.GetType(Self.ClassType).GetFields do
    Result := Result + Format('%s: %s = %s', [Field.Name, Field.FieldType.ToString, Field.GetValue(Self).ToString]) + sLineBreak;
end;
于 2017-03-29T15:42:00.870 回答