Я скомпоновал небольшой модуль, облегчающий чтение и запись в поток
объектов-не-компонентов. За основу взят базовый класс (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.
|
|