gpt4 book ai didi

delphi - 如何在 Delphi XE 中使用 RTTI 设置记录值

转载 作者:行者123 更新时间:2023-12-03 18:00:18 24 4
gpt4 key购买 nike

我正在尝试在 Delphi XE 中使用 RTTI 设置记录的值。我可以使用 GetValue 方法从记录中获取值,但不能使用 SetValue 方法设置值。

有谁知道该怎么做/为什么它不起作用?

提前致谢!

我的背景:最终目标是编写一个组件,该组件将读取任何 XML 文件并使用 XML 中的数据自动填充应用程序的数据模型。数据模型将被注释以确定所有元素的 XPath。对于对象和基本数据类型,我已经启动并运行了。

  TSize = record
X, Y: double;
end;

TMyTest = class
protected
FSize: TSize;
public
constructor Create;
procedure DoStuff;
end;

constructor TMyTest.Create;
begin
FSize.X := 2.7;
FSize.Y := 3.1;
end;

procedure TMyTest.DoStuff;
var
MyContext: TRttiContext;
MyField: TRttiField;
MySizeField: TRttiField;
MyVal: TValue;
MyRecord: TRttiRecordType;
NewVal: TValue;
begin
// Explicit Create of MyContext does not help (as expected)
for MyField in MyContext.GetType(ClassType).GetFields do
if MyField.Name = 'FSize' then //For debugging
begin
MyRecord := MyField.FieldType.AsRecord;
MyVal := MyField.GetValue(Self);
for MySizeField in MyRecord.GetFields do
begin
//This works
NewVal := MySizeField.GetValue(MyVal.GetReferenceToRawData).AsExtended;
NewVal := NewVal.AsExtended + 5.0;
try
// This does not work. (no feedback)
MySizeField.SetValue(MyVal.GetReferenceToRawData, NewVal);
// This however does work. Now to find out what the difference between the two is.
MySizeField.SetValue(@FSize, NewVal);
except
on e: Exception do //Never happens
ShowMessage('Oops!' + sLineBreak + e.Message);
end;
end;
end;
// Shows 'X=2.7 Y=3.1'
// Expected 'X=7.7 Y=8.1'
ShowMessage(Format('X=%f Y=%f', [FSize.X, FSize.Y]));
end;

如果 TSize 声明为类,则代码中的 MyVal.GetReferenceToRawData 应替换为 TObject(MyVal.GetReferenceToRawData^)。如果您这样做,一切都会按预期进行。 (是的,MyVal.AsObject 在这种情况下也可以解决问题)这使我想到了一个可能的解决方案:将 MyVal.GetReferenceToRawData^ 类型转换为正确的记录类型。这怎么能做到呢?

我刚刚尝试在 SetValue 中直接使用 @FSize。如您所料,这有效。这引发了一个问题:@FSize 和 MyVal.GetReferenceToRawData 之间有什么不同


经过大量进一步调查,我发现 MyVal 实际上是记录的副本,因此确实如 Serg 在他的第一个答案中提到的那样正确设置了值,但是,它是在副本中设置的。我为没有早点意识到这一点而感到很愚蠢......

无论如何,下面是一个有效的代码示例。此外,如果您愿意使用“字段名路径”,您可以查看 Barry Kelly 在 this 中的方法。帖子,这让我走上正轨。我只是不喜欢 Follow 例程所需的路径。

procedure TMyTest.DoStuff;
var
MyContext: TRttiContext;
MyField: TRttiField;
MySizeField: TRttiField;
NewVal: TValue;
dMyVal: double;
begin
for MyField in MyContext.GetType(ClassType).GetFields do
if MyField.Name = 'FSize' then
begin
for MySizeField in MyField.FieldType.GetFields do
begin
dMyVal := MySizeField.GetValue(PByte(Self) + MyField.Offset).AsExtended;
NewVal := TValue.From(dMyVal + 5.1);
try
MySizeField.SetValue(PByte(Self) + MyField.Offset, NewVal);
except
on e: Exception do
ShowMessage('Oops!' + sLineBreak + e.Message);
end;
end;
end;
if FSize.X > 5.0 then
ShowMessage(Format('X=%f Y=%f', [FSize.X, FSize.Y]));
end;

最佳答案

MyVal.GetReferenceToRawData 不会返回指向 FSize 字段的指针,这就是 FSize 字段值不会更改的原因。

我不确定我的代码是你想写的,但我希望它会有所帮助:

uses rtti, typinfo;

type
TSize = record
X, Y: double;
end;

TMyTest = class
protected
FSize: TSize;
public
constructor Create;
procedure DoStuff;
end;

constructor TMyTest.Create;
begin
FSize.X := 2.7;
FSize.Y := 3.1;
end;

procedure TMyTest.DoStuff;
var
MyContext: TRttiContext;
MyField: TRttiField;
MyVal: TValue;
NewSize: TSize;

begin
// Explicit Create of MyContext does not help (as expected)
for MyField in MyContext.GetType(ClassType).GetFields do
if MyField.Name = 'FSize' then //For debugging
begin
MyVal := MyField.GetValue(Self);
NewSize:= MyVal.AsType<TSize>;
NewSize.X:= NewSize.X + 5;
NewSize.Y:= NewSize.Y + 5;
TValue.Make(@NewSize, TypeInfo(TSize), MyVal);
MyField.SetValue(Self, MyVal);
end;
ShowMessage(Format('X=%f Y=%f', [FSize.X, FSize.Y]));
end;

procedure TForm1.Button1Click(Sender: TObject);
var
Test: TMyTest;

begin
Test:= TMyTest.Create;
Test.DoStuff;
Test.Free;
end;

关于delphi - 如何在 Delphi XE 中使用 RTTI 设置记录值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10296547/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com