Delphi: 如何获取静态记录的地址

Delphi: How to get the address of a static record

    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 变成 return:

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

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

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

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

如另一个答案所述,无法使用 RTTI 访问 class 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 

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

与此同时,我想出了另一个解决方案,尽管它使用 class(带有外部实例)而不是记录,我不想这样做,但我没有'看不到其他方式,所以感谢您的解决方案!

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;