Сериализация TCollection, которая не объявлена в TComponent?
Можно ли сериализовать 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 ответа
Вы можете сериализовать TCollection, не инкапсулированный в TComponent, с помощью другого потомка 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), выполняющим сериализацию во время разработки.
Это работает, только если вы помещаете свою коллекцию в TComponent, потому что TMemoryStream.WriteComponent (само имя является подсказкой!) Принимает TComponent в качестве параметра:
procedure WriteComponent(Instance: TComponent);
и TCollection, как вы уже обнаружили, не является потомком TComponent. Может показаться странным, что потомок TComponent просто содержит вашего потомка TCollection, но если вы хотите передавать его с помощью потоковых средств WriteComponent, я не вижу другого простого способа сделать это.
Если вы хотите сделать это, используя "просто" RTL/VCL (т.е. не используя стороннюю библиотеку), вам придется написать потомок T(Memory)Stream и добавить реализацию WritePersistent, которая принимает Instance: TPersistent
параметр.
Я не слишком много углублялся в классы TStream, но я предполагаю, что вы сможете многое позаимствовать у поддержки TComponent. Конечно, поддержка наследования классов.
На первый взгляд кажется поверхностным WriteComponent
просто звонки WriteDescendent
который создает TWriter
а затем вызывает WriteDescendent
метод этого писателя. И TWriter уже содержит методы для написания коллекции.
Однако, если вы "просто" хотите транслировать потомков TPersistent, вам придется проделать большую работу в TWriter / TReader, так как они полностью основаны на TComponent. И это не будет простым случаем просто написать пару потомков. С одной стороны, они TWriter / TReader на самом деле не настроены для получения. Для другого: TStream (потомки) создают экземпляры TWriter и TReader напрямую, и у этих классов нет виртуальных конструкторов. Что делает написание потомков для них довольно бесполезным, если вы не хотите попробовать свои силы в подключении, исправлении VMT и других интересных вещах.
В общем, самый простой способ для потоковой передачи вашей пользовательской коллекции - это просто "обернуть" ее в TComponent и жить с "пустыми" из этого.