Has anyone an idea, how I can make TValue using a reference to the original data? In my serialization project, I use (as suggested in XML-Serialization) a generic serializer which stores TValues in an internal tree-structure (similar to the MemberMap in the example).
This member-tree should also be used to create a dynamic setup form and manipulate the data. My idea was to define a property for the Data:
TDataModel <T> = class
{...}
private
FData : TValue;
function GetData : T;
procedure SetData (Value : T);
public
property Data : T read GetData write SetData;
end;
The implementation of the GetData, SetData Methods:
procedure TDataModel <T>.SetData (Value : T);
begin
FData := TV开发者_高级运维alue.From <T> (Value);
end;
procedure TDataModel <T>.GetData : T;
begin
Result := FData.AsType <T>;
end;
Unfortunately, the TValue.From method always makes a copy of the original data. So whenever the application makes changes to the data, the DataModel is not updated and vice versa if I change my DataModel in a dynamic form, the original data is not affected. Sure I could always use the Data property before and after changing anything, but as I use lot of Rtti inside my DataModel, I do not realy want to do this anytime.
Perhaps someone has a better suggestion?
A TValue is designed to hold any kind of data in a very compact form, it's not designed to emulate an "pointer". Take a look in the RTTI.pas unit: TValue is a RECORD that only has one data member of the type TValueData. TValueData itself is itself a variant record.
Looking at TValueData you will see how it does NOT hold anything but the minimum amount of data: There's no way to know where that TValue came from.
The solution: Don't hold a TValue in your structures, replace it with a pair of TRttiField + an TObject. When you need the TValue use TRttiField.GetValue(Instance), when you want to set a value use TRttiField.SetValue(Instance, AValue:TValue).
Thanks Cosmin for your help, the solution is not to save a TValue in the structure but use a Pointer to the data and use the GetValue, SetValue methods of a field or property.
So here is how I solved it in my generic class:
TDataModel <T> = class
private
FType : PTypeInfo;
FInstance : Pointer;
public
constructor Create (var Data : T);
procedure ShowContent;
end;
constructor TDataModel <T>.Create (var Data : T);
begin
FType := TypeInfo(T);
if FType.Kind = tkClass then
FInstance := TObject (Data)
else if FType.Kind = tkRecord then
FInstance := @Data;
end;
procedure TDataModel <T>.ShowContent;
var
Ctx : TRttiContext;
Field : TRttiField;
begin
for Field in Ctx.GetType (FType).GetFields do
Writeln (Field.GetValue (FInstance).ToString);
end;
Now you can either change fields using the DataModel through or using the original record class.
精彩评论