Я скомпоновал небольшой модуль, облегчающий чтение и запись в поток объектов-не-компонентов. За основу взят базовый класс (TStreamable), имеющий пустые процедуры Load и Store, перекрываемые в потомках для выполнения специфических задач, плюс некоторые расширения классов TReader и TWriter, автоматизирующие работу с системными регистрами. Вы увидите это в нижней части этого совета. Надеюсь, вы найдете это полезным.
Процедуры Load и Store класса TStreamable и его потомков содержат параметры, делающие возможным чтение и запись методами классов TReader и TWriter (смотри файл помощи Component Writer's Help). Сохраняемая сущность меняется от класса к классу, поэтому для себя вы должны решить с чем вы будете работать.
Все классы, с которыми вы собираетесь работать через поток, должны быть зарегистрированы вызовом RegisterClass. Лично я это делаю в секции инициализации модуля, в котором определен данный класс.
Если еще во время разработки приложения вы знаете, объект какого класса вы будете сохранять/загружать и все, что вам нужно сделать, это инициализировать поток, filer и экземпляр объекта (только загрузка), то для этого можно использовать метод Load/Store.
Пример: (сохранение, FKnowObject - поле в TTest)
procedure TTest.StoreKnownObject(Filename: string); var S: TFileStream; W: TEnhWriter; begin S := TFileStream.Create(Filename, fmOpenWrite); W := TEnhWriter.Create(S, 1024); FKnownObject.Store(W); W.Free; S.Free; end; |
(загрузка объекта, сохраненного выше, предполагает, что до этого момента был осуществлен вызов FKnowObject.Create)
procedure TTest.LoadKnownObject(Filename: string); var S: TFileStream; R: TEnhReader; begin S := TFileStream.Create(Filename, fmOpenRead; R := TEnhReader.Create(S, 1024); FKnownObject.Load(R); {перезаписываем все предыдущие данные объекта} R.Free; S.Free; end; |
Имея список объектов, проще всего работать с ними в цикле.
Пример: (сохранение, список TKnownObjects)
procedure TTest.StoreKnownList(Filename: string; List: TList); var S: TFileStream; W: TEnhWriter; I: Integer; begin S := TFileStream.Create(Filename, fmOpenWrite; W := TEnhWriter.Create(S, 1024); W.WriteListBegin; with List do for I := 0 to Count -1 do TKnownObject(Items[I]).Store(W); W.WriteListEnd; W.Free; S.Free; end; |
(загрузка выше в пустой список)
procedure TTest.StoreKnownList(Filename: string; List: TList); var S: TFileStream; R: TEnhReader; K: TKnownObject; begin S := TFileStream.Create(Filename, fmOpenRead; R := TEnhReader.Create(S, 1024); R.ReadListBegin; with List do while not EndOfList do begin K.Create; K.Load(R); Add(K); end; R.ReadListEnd; end; |
Методы, которые я добавил к TReader/TWriter, действительно начинают работать только тогда, когда вы имеете дело с объектами двух и более классов (предок и потомок, разные классы от одного родителя и т.п.).
function TEnhReader.ReadStreamable: TStreamable |
Читает из потока имя класса, вызывает FindClass для получения класса и затем создает его экземпляр. Затем вызывает Load для чтения данных объекта. Вероятно вы захотите объявить указатель на возвращаемое значение, имеющее тип последнего общего из всех возможных предков, *не* инициализуруйте это заранее.
procedure TEnhReader.ReadListItems(List: TList); |
Список TList должен быть инициализированным, но пустым (будет вызван метод TList.Clear). Читает маркер StartOfList, затем вызывает ReadStreamable до тех пор, пока не достигнет маркера EndOfList, добавляет возвращаемые объекты в список к текущей позиции. Затем читает маркер EndOfList.
procedure TEnhWriter.WriteStreamable(AObj: TStreamable); |
Записывает имя класса объекта в поток, затем вызывает Store для записи данных.
procedure TEnhWriter.WriteListItems(List: TList); |
Записывает маркер StartOfList, в цикле приводит элементы списка к типу TStreamable и вызывает WriteStreamable для их записи. И, наконец, записывает маркер в конец списка.
--Вырезано--
{Базовый класс и его расширения для работы с потоковыми объектами} {Авторские права принадлежат Don Croyle} unit Strmstuf; interface uses Classes; type TEnhReader = class; TEnhWriter = class; TStreamable = class(TPersistent) public procedure Load(R: TEnhReader); virtual; procedure Store(W: TEnhWriter); virtual; end; TStreamableClass = class of TStreamable; TEnhReader = class(TReader) public procedure ReadListItems(List: TList); function ReadStreamable: TStreamable; end; TEnhWriter = class(TWriter) public procedure WriteListItems(List: TList); procedure WriteStreamable(AObj: TStreamable); end; implementation procedure TStreamable.Load(R: TEnhReader); begin end; procedure TStreamable.Store(W: TEnhWriter); begin end; procedure TEnhReader.ReadListItems(List: TList); begin ReadListBegin; with List do begin Clear; while not EndOfList do Add(ReadStreamable); end; ReadListEnd; end; function TEnhReader.ReadStreamable: TStreamable; begin Result := TStreamableClass(FindClass(ReadString)).Create; if Result <> nil then Result.Load(Self); end; procedure TEnhWriter.WriteListItems(List: TList); var I: Integer; begin WriteListBegin; with List do for I := 0 to Count -1 do WriteStreamable(TStreamable(Items[I])); WriteListEnd; end; procedure TEnhWriter.WriteStreamable(AObj: TStreamable); begin WriteString(AObj.ClassName); AObj.Store(Self); end; end. |
--Вырезано-- [000576]