1

是否可以序列化未封装在 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 中的情况下进行流式传输?

4

2 回答 2

1

您可以通过定义如下的另一个 TComponent 后代序列化未封装在 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 实现)一起注册。

于 2012-01-14T18:18:31.420 回答
1

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

procedure WriteComponent(Instance: TComponent);

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


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

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

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

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

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

于 2012-01-14T15:21:51.640 回答