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;

DefinePropertiesWriteData、およびReadDataの実装の詳細:

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;

LoadFromCollectionおよびSaveToCollectionのテンプレート:

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について:

パラメータとしてTCollectionを持つリッチ クリエータを持ち、 TFileStreamのように動作するように設計された TStream の子孫である必要があります。実装する必要があります。免責事項:私はそれをテストしたことはありませんが、TFileStream が機能することはわかります (外部ファイルのストリーミング用)。

結論:

このコンポーネントは、Delphi XE (RCData) で外部ファイルを DFM 内でシリアル化する VCL の方法に着想を得ています。設計時にシリアル化を行うコンポーネント エディタ(TComponentEditor に基づいて実装する必要もあります)とともに登録する必要があります。

于 2012-01-14T18:18:31.420 に答える
1

コレクションを TComponent に配置した場合にのみ機能します。これは、TMemoryStream.WriteComponent (名前自体がヒントです!) が TComponent をパラメータとして受け取るためです。

procedure WriteComponent(Instance: TComponent);

TCollection は、TComponent の子孫ではないことをすでに発見したとおりです。TCollection の子孫を保持するためだけに TComponent の子孫を持つのは奇妙に思えるかもしれませんが、ストリームの WriteComponent 機能を使用してストリーミングしたい場合、それを行う簡単な方法は他にありません。


RTL/VCL のみを使用してこれを行う場合 (つまり、サード パーティのライブラリを使用しない場合)、T(Memory)Stream の子孫を記述し、パラメーターを受け取る WritePersistent 実装を追加する必要がありInstance: TPersistentます。

私はTStreamクラスについてはあまり深く掘り下げていませんが、TComponent サポートから多くのことを借りることができるはずです。確かにクラス継承のサポート。

ざっと見てみると、最初は単にをインスタンス化し、そのライターのメソッドを呼び出すWriteComponentだけのように思えます。また、TWriter には、コレクションを書き込むためのメソッドが既に含まれています。WriteDescendentTWriterWriteDescendent

ただし、TPersistent の子孫を「単に」ストリーミングしたい場合は、TWriter/TReader で多くの作業を行う必要があり、それらは完全に TComponent に基づいています。そして、子孫のカップルを書くだけの単純なケースではありません。1 つは、それらの TWriter/TReader が実際に派生するように設定されていないことです。別の例: TStream (子孫) は TWriter と TReader を直接インスタンス化し、これらのクラスには仮想コンストラクターがありません。そのため、フックや VMT へのパッチ適用などの興味深い機能を試してみたい場合を除き、それらの子孫を作成することはかなり無駄になります。

全体として、カスタム コレクションをストリーミングする最も簡単な方法は、それを TComponent で「ただ」ラップし、その「無駄」を受け入れることです。

于 2012-01-14T15:21:51.640 に答える