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'('期望但是')'找到了
通常应使用记录的“实例”的地址调用GetValue。但在这种情况下,记录是静态的。 我不想将此记录转换为正常记录,创建实例等。我正在尝试为静态记录解决此问题。 如何获取应传递给GetValue的地址?
答案 0 :(得分:3)
据我所知,无法通过RTTI访问课程字段。
答案 1 :(得分: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
答案 2 :(得分:0)
谢谢LU RD的贡献!非常聪明,使用嵌套记录并声明本地类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;