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;
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;