【问题标题】:Serialization of a TCollection which is not declared in a TComponent?未在 TComponent 中声明的 TCollection 的序列化?
【发布时间】:2012-02-10 09:23:25
【问题描述】:

是否可以序列化未封装在 TComponent 中的 TCollection ?

例如,我有一个自定义 TCollection。我不能在我的 TCollection 后代上使用 TMemoryStream.WriteComponent() 。只有将集合封装在 TComponent 中,然后再编写此组件时,它才会起作用。

技术上没有问题,但声明一个拥有 TCollection 的 TComponent 似乎有点奇怪。

TMyCustomCollection = Class(TCollection) // not serializable ?
  //...
End;

TMyCustomCollectionCapsule = Class(TComponent) // serializable !
Private
  FMyCusColl: TMyCustomCollection;
  Procedure SetMyCusColl(Const Data: TMyCustomCollection);
Published
  Property CanBeSerialized: TMyCustomCollection Read FMyCusColl Write SetMyCusColl
End;

也许我只是错过了 Delphi RTL 的一个功能? TPersistent 后代是否可以在不封装在 TComponent 中的情况下进行流式传输?

【问题讨论】:

  • 如果 XML 是一个选项,我强烈推荐 OmniXML(omnixml.com),因为简单,我在很多项目中使用它,1 调用加载它,1 调用保存它,简单而高效。

标签: serialization persistence delphi tcollection


【解决方案1】:

仅当您将集合放入 TComponent 时才有效,因为 TMemoryStream.WriteComponent(名称本身就是一个线索!)将 TComponent 作为参数:

procedure WriteComponent(Instance: TComponent);

并且 TCollection 就像您已经发现的那样,不是 TComponent 的后代。拥有一个 TComponent 后代只是为了保存您的 TCollection 后代可能看起来很奇怪,但如果您想使用 Streams 的 WriteComponent 工具对其进行流式传输,我看不出有任何其他简单的方法可以做到这一点。


如果您想“仅”使用 RTL/VCL(即不使用第三方库)来执行此操作,则必须编写一个 T(Memory)Stream 后代并添加一个采用 Instance: TPersistent 的 WritePersistent 实现范围。

我还没有深入深入研究 TStream 类,但我猜你应该能够从 TComponent 支持中借用很多东西。当然是类继承支持。

粗略看了一下,乍一看似乎很简单,因为WriteComponent 只是调用WriteDescendent,它实例化了TWriter,然后调用了该作者的WriteDescendent 方法。而且 TWriter 已经包含了编写集合的方法。

但是,如果您“只是”想要流式传输 TPersistent 后代,则必须在 TWriter/TReader 中做很多工作,因为它们完全基于 TComponent。这不会是仅仅写几个后代的简单案例。一方面,它们的 TWriter/TReader 并未真正设置为派生自。另一个:TStream(后代)直接实例化 TWriter 和 TReader,这些类没有虚拟构造函数。这使得为​​它们编写后代相当徒劳,除非您想尝试挂钩、修补 VMT 以及更多有趣的东西。

总而言之:流式传输您的自定义集合的最简单方法仍然是将其“仅”包装在 TComponent 中并忍受它的“浪费”。

【讨论】:

  • 是的,我注意到了这一点,但我的问题与“如何做”更相关。有人发布了另一个建议使用第 3 部分库的答案...但同行已删除该帖子...我认为在 RTL 中是可能的...但是使用胶囊而不是处理RTTI...
  • @az01:是的,我明白了,但是“如何”的答案并不是那么简单,当然如果您只想使用 RTL/VCL。请参阅我的答案的更新。
  • 问你的小窍门,Az01:如果你想知道如何做某事,那就不要问简单的。不要问“我可以做 X 吗?”问“我如何做 X?”如果事实证明你实际上根本不能做 X,那么无论如何你都会得到那个答案。如果您从是/否问题开始,只需假设答案就是您想要的答案,然后直接跳到自然的后续问题。
【解决方案2】:

您可以通过 another TComponent descendant 将未封装在 TComponent 中的 TCollection 序列化,定义如下:

type
  TCollectionSerializer = class(TComponent)
  protected
    FCollectionData: string;
    procedure DefineProperties(Filer: TFiler); override;
  public
    procedure WriteData(Stream: TStream);
    procedure ReadData(Stream: TStream);
    //
    procedure LoadFromCollection(ACollection: TCollection);
    procedure SaveToCollection(ACollection: TCollection);
  end;

DefinePropertiesWriteDataReadData 实现细节:

procedure TCollectionSerializer.WriteData(Stream: TStream);
var
  StrStream: TStringStream;
begin
  StrStream:=TStringStream.Create;
  try
    StrStream.WriteString(FCollectionData);
    Stream.CopyFrom(StrStream,0);
  finally
    StrStream.Free;
  end;
end;

procedure TCollectionSerializer.ReadData(Stream: TStream);
var
  StrStream: TStringStream;
begin
  StrStream:=TStringStream.Create;
  try
    StrStream.CopyFrom(Stream,0);
    FCollectionData:=StrStream.DataString;
  finally
    StrStream.Free;
  end;
end;

procedure TCollectionSerializer.DefineProperties(Filer: TFiler);
begin
  inherited;
  //
  Filer.DefineBinaryProperty('CollectionData', ReadData, WriteData,True);
end;

LoadFromCollectionSaveToCollection 的模板:

procedure TCollectionSerializer.LoadFromCollection(ACollection: TCollection);
var
  CollectionStream: TStream;
begin
  CollectionStream:= TCollectionStream.Create(ACollection);
  try
    ReadData(CollectionStream)
  finally
    CollectionStream.Free;
  end;
end;

procedure TCollectionSerializer.SaveToCollection(ACollection: TCollection);
var
  CollectionStream: TStream;
begin
  CollectionStream:= TCollectionStream.Create(ACollection);
  try
    WriteData(CollectionStream);
  finally
    CollectionStream.Free;
  end;
end;

关于TCollectionStream

它应该是一个 TStream 后代,它有一个丰富的创建者,以 TCollection 作为参数,并且设计为像 TFileStream 一样工作。你必须实施它。 免责声明:我从未对此进行过测试,但我可以看出 TFileStream 可以工作(用于流式传输外部文件)。

结论:

这个组件的灵感来自于在 DFM 中序列化 Delphi XE (RCData) 下的外部文件的 VCL 方式。它必须与在设计时进行序列化的组件编辑器(您还必须基于 TComponentEditor 实现)一起注册。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-18
    • 2021-08-31
    • 2015-03-26
    • 2018-06-05
    相关资源
    最近更新 更多