Delphi 2010 中的 Rtti 数据操作和一致性

发布于 2024-08-31 21:33:32 字数 959 浏览 7 评论 0原文

有谁知道如何使用对原始数据的引用来制作 TValue ?在我的序列化项目中,我使用(如 XML-Serialization )一个通用序列化器,它将 TValue 存储在内部树结构中(类似于示例中的 MemberMap)。

该成员树还应用于创建动态设置表单并操作数据。 我的想法是为数据定义一个属性:

TDataModel <T> = class
  {...}
  private
    FData : TValue;
    function GetData : T;
    procedure SetData (Value : T);
  public
    property Data : T read GetData write SetData;
end;

GetData、SetData 方法的实现:

procedure TDataModel <T>.SetData (Value : T);

begin
FData := TValue.From <T> (Value);
end;

procedure TDataModel <T>.GetData : T;

begin
Result := FData.AsType <T>;
end;

不幸的是,TValue.From 方法总是复制原始数据。因此,每当应用程序对数据进行更改时,DataModel 都不会更新,反之亦然,如果我以动态形式更改 DataModel,原始数据不会受到影响。 当然,我总是可以在更改任何内容之前和之后使用 Data 属性,但由于我在 DataModel 中使用了很多 Rtti,所以我真的不想在任何时候都这样做。

也许有人有更好的建议?

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 := TValue.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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

恋竹姑娘 2024-09-07 21:33:32

TValue 旨在以非常紧凑的形式保存任何类型的数据,它不是为了模拟“指针”而设计的。看一下 RTTI.pas 单元:TValue 是一个 RECORD,只有一个 TValueData 类型的数据成员。 TValueData 本身就是一个变体记录。

查看 TValueData,您会发现它除了最小量的数据外不保存任何内容:无法知道 TValue 来自何处。

解决方案:不要在结构中保留 TValue,而是用一对 TRttiField + 一个 TObject 替换它。当您需要 TValue 时,请使用 TRttiField.GetValue(Instance),当您想要设置值时,请使用 TRttiField.SetValue(Instance, AValue:TValue)。

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).

花开柳相依 2024-09-07 21:33:32

感谢 Cosmin 的帮助,解决方案不是在结构中保存 TValue,而是使用指向数据的指针并使用字段或属性的 GetValue、SetValue 方法。

以下是我在通用类中解决该问题的方法:

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;

现在,您可以通过使用 DataModel 或使用原始记录类来更改字段。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文