I am trying to set record values using RTTI in Delphi XE. I can get the value from the record using the GetValue method, but I can not set the value using the SetValue method.
Does anyone know how to do this / why it doesn't work?
Thanks in advance!
My context: The ultimate goal is to write a component that will read any XML file and automatically populate the application data model with XML data. Dunmatode will be annotated to determine XPaths for all elements. For objects and basic data types, I already have it and it works.
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 )
: Typecast MyVal.GetReferenceToRawData^ . ?
@FSize SetValue. , .
: @FSize MyVal.GetReferenceToRawDatap >
, MyVal , , , . , ...
, . , " ", , . , 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;