Сериализация TCollection, которая не объявлена ​​в TComponent? - PullRequest
1 голос
/ 14 января 2012

Можно ли сериализовать TCollection, которая не инкапсулирована в TComponent?

Например, у меня есть пользовательская коллекция TCollection. Я не могу использовать TMemoryStream.WriteComponent () на моем потомке TCollection. Это будет работать, только если я инкапсулирую коллекцию в TComponent, а затем, если я напишу этот компонент.

Технически проблем нет, но объявление TComponent, которому только принадлежит TCollection, кажется немного странным.

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?

Ответы [ 2 ]

1 голос
/ 14 января 2012

Вы можете сериализовать коллекцию TC, не инкапсулированную в компонент TC, с помощью другого Потомка TComponent , определенного следующим образом:

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;

DefineProperties , WriteData и 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 :

Это должен быть потомок TStream, имеющий богатого создателя с TCollection в качестве параметра и предназначенный для поведения, подобного TFileStream,Вы должны реализовать это. Отказ от ответственности: Я никогда не проверял это, но могу сказать, что TFileStream работает (для потоковой передачи внешнего файла).

Вывод:

Этот компонентвдохновлен способом VCL для сериализации в DFM внешнего файла под Delphi XE (RCData).Он должен быть зарегистрирован вместе с редактором компонентов (который вы также должны реализовать на основе TComponentEditor), выполняющим сериализацию во время разработки.

1 голос
/ 14 января 2012

Это работает, только если вы поместите свою коллекцию в TComponent, потому что TMemoryStream.WriteComponent (само имя является подсказкой!) Принимает TComponent в качестве параметра:

procedure WriteComponent(Instance: TComponent);

и TCollection, как вы уже обнаружили, не является потомком TComponent. Может показаться странным, что потомок TComponent просто содержит вашего потомка TCollection, но если вы хотите передать его с помощью потоковых средств WriteComponent, я не вижу другого простого способа сделать это.


Если вы хотите сделать это, используя «просто» RTL / VCL (т.е. не используя стороннюю библиотеку), вам придется написать потомок T (Memory) Stream и добавить реализацию WritePersistent, которая принимает Instance: TPersistent параметр.

Я не delphed так много углублялся в классы TStream, но я предполагаю, что у вас будет много возможностей позаимствовать у поддержки TComponent. Конечно, поддержка наследования классов.

При поверхностном взгляде на первый взгляд кажется простым, что WriteComponent просто вызывает WriteDescendent, который создает экземпляр TWriter, а затем вызывает метод WriteDescendent этого писателя. И TWriter уже содержит методы для написания коллекции.

Однако, если вы «просто» хотите транслировать потомков TPersistent, вам придется проделать большую работу в TWriter / TReader, так как они полностью основаны на TComponent. И это не будет простым случаем просто написать пару потомков. С одной стороны, они TWriter / TReader на самом деле не настроены для получения. Для другого: TStream (потомки) создают экземпляры TWriter и TReader напрямую, и у этих классов нет виртуальных конструкторов. Что делает написание потомков для них довольно бесполезным, если вы не хотите попробовать свои силы в подключении, исправлении VMT и других интересных вещах.

В общем и целом: самый простой способ потоковой передачи вашей пользовательской коллекции - просто "обернуть" ее в компонент TComon и жить с "ненужными" последствиями.

...