TStream как объект внутри StringList
Я использую Delphi 7 и играю со StringList, с TStream в качестве объекта.
Мой тестовый проект имеет ListBox, Memo и 2 кнопки (Add and Remove).
Вот что я получил так далеко:
var
List: TStringList;
procedure TForm1.FormCreate(Sender: TObject);
begin
List := TStringList.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
I: Integer;
begin
if (List.Count > 0) then
for I := 0 to Pred(List.Count) do
begin
List.Objects[I].Free;
List.Objects[I] := nil;
end;
FreeAndNil(List);
end;
procedure TForm1.btnAddClick(Sender: TObject);
var
Strm: TStream;
begin
Strm := TMemoryStream.Create;
try
Memo.Lines.SaveToStream(Strm);
List.AddObject(IntToStr(List.Count), TObject(Strm));
Memo.Clear;
ListBox.Items.Assign(List);
finally
// Strm.Free; (line removed)
end;
end;
procedure TForm1.btnDelFirstClick(Sender: TObject);
begin
if (List.Count > 0) then
begin
List.Objects[0].Free;
List.Objects[0] := nil;
List.Delete(0);
ListBox.Items.Assign(List);
end;
end;
Когда я дважды щелкаю ListBox, я хотел бы загрузить выбранный элемент объекта Stream в Memo. Вот что я пытался сделать:
procedure TForm1.ListBoxDblClick(Sender: TObject);
var
Idx: Integer;
begin
Memo.Clear;
Idx := ListBox.ItemIndex;
if (Idx >= 0) and (TStream(List.Objects[Idx]).Size > 0) then
Memo.Lines.LoadFromStream(TStream(List.Objects[Idx]));
end;
Мои вопросы:
Правильно ли я добавляю и удаляю (освобождаю) объект TStream внутри StringList? Может быть, мне нужно сначала освободить поток, а затем объект??
Правильно ли я освобождаю все объекты в событии FormDestroy?
Когда я пытаюсь загрузить поток обратно в Memo (Memo.Lines.LoadFromStream(TStream(List.Objects[Idx]))), он не загружается, несмотря на то, что Stream.Size больше нуля. Что я делаю не так?
2 ответа
1. Правильно ли я добавляю и удаляю (освобождаю) объект TStream внутри StringList?
Да, потому что TStrings.Objects[]
свойство возвращает TObject
указатель и TStream
происходит от TObject
так что вы можете позвонить Free()
на указатели объекта.
Может быть, мне нужно сначала освободить поток, а затем объект??
Вам нужно освободить TStream
объекты до освобождения TStringList
объект. Так же, как вы уже делаете.
2. Правильно ли я освобождаю все объекты в событии FormDestroy?
Да. Хотя технически вам не нужно проверять TStringList.Count
собственность для > 0
перед входом в цикл, так как цикл будет обрабатывать это условие для вас. И вам не нужно nil
указатели перед освобождением TStringList
:
procedure TForm1.FormDestroy(Sender: TObject);
var
I: Integer;
begin
for I := 0 to Pred(List.Count) do
List.Objects[I].Free;
List.Free;
end;
Одна вещь, которую вы делаете, это перебор Assign()
в целом TStringList
к TListBox
всякий раз, когда вы добавляете / удаляете один элемент из TStringList
, Вместо этого вы должны просто добавить / удалить связанный элемент из ListBox и сохранить оставшиеся элементы как есть.
И добавить дополнительную проверку ошибок в btnAddClick()
также, чтобы избежать утечек памяти, если что-то пойдет не так.
Попробуй это:
procedure TForm1.btnAddClick(Sender: TObject);
var
Strm: TStream;
Idx: Integer;
begin
Strm := TMemoryStream.Create;
try
Memo.Lines.SaveToStream(Strm);
Strm.Position := 0;
Idx := List.AddObject(IntToStr(List.Count), Strm);
except
Strm.Free;
raise;
end;
try
ListBox.Items.Add(List.Strings[Idx]);
except
List.Objects[Idx].Free;
List.Delete(Idx);
raise;
end;
Memo.Clear;
end;
procedure TForm1.btnDelFirstClick(Sender: TObject);
begin
if List.Count > 0 then
begin
List.Objects[0].Free;
List.Delete(0);
ListBox.Items.Delete(0);
end;
end;
3. Когда я пытаюсь загрузить поток обратно в Memo (Memo.Lines.LoadFromStream(TStream(List.Objects[Idx]))), он не загружается, несмотря на то, что Stream.Size больше нуля. Что я делаю не так?
Вы не ищете поток обратно Position
0 перед загрузкой в Заметку. SaveToStream()
всегда покидает поток, расположенный в конце потока, и LoadFromStream()
оставьте поток в том месте, где загрузка прекратила чтение (если не в конце, в случае сбоя).
Теперь со всем сказанным я лично не буду пользоваться TListBox
таким образом. Я бы вместо этого установил его Style
собственность на lbVirtual
а затем использовать его OnData
событие для отображения строк из TStringList
, Нет необходимости копировать их в TListBox
напрямую или всегда пытайтесь синхронизировать два списка. Было бы безопаснее, и использовать меньше памяти, чтобы TListBox
спросить вас, что ему нужно, а затем вы можете предоставить его из TStringList
(который я бы тогда изменил на TList
так как вы на самом деле не храните значимые имена, которые не могут быть созданы динамически в OnData
обработчик события):
var
List: TList;
procedure TForm1.FormCreate(Sender: TObject);
begin
List := TList.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
I: Integer;
begin
ListBox.Count := 0;
for I := 0 to Pred(List.Count) do
TStream(List[I]).Free;
List.Free;
end;
procedure TForm1.btnAddClick(Sender: TObject);
var
Strm: TStream;
Idx: Integer;
begin
Strm := TMemoryStream.Create;
try
Memo.Lines.SaveToStream(Strm);
Strm.Position := 0;
Idx := List.Add(Strm);
except
Strm.Free;
raise;
end;
try
ListBox.Count := List.Count;
except
TStream(List[Idx]).Free;
List.Delete(Idx);
raise;
end;
Memo.Clear;
end;
procedure TForm1.btnDelFirstClick(Sender: TObject);
begin
if List.Count > 0 then
begin
TStream(List[0]).Free;
List.Delete(0);
ListBox.Count := List.Count;
end;
end;
procedure TForm1.ListBoxDblClick(Sender: TObject);
var
Strm: TStream;
Idx: Integer;
begin
Memo.Clear;
Idx := ListBox.ItemIndex;
if Idx >= 0 then
begin
Strm := TStream(List[Idx]);
if Strm.Size > 0 then
begin
Strm.Position := 0;
Memo.Lines.LoadFromStream(Strm);
end;
end;
end;
procedure TForm1.ListBoxData(Control: TWinControl; Index: Integer; var Data: string);
begin
Data := IntToStr(Index);
end;
Я не понимаю, что вы предлагаете по поводу освобождения потока, а затем объекта. Насколько я понимаю, объект, о котором вы говорите - это поток. Вы не можете уничтожить один раньше другого, потому что есть только один объект, который является потоком.
Ваши методы добавления и удаления потоковых объектов в списке строк хороши. Они не идеальны, но я ограничу свои комментарии здесь, потому что переполнение стека не является Code Review.
После звонка
SaveToStream
, позиция потока находится в конце потока. Если вы хотите читать из потока, вам придется снова установить позицию на начало. ЗадаватьPosition := 0
для потока до вызоваLoadFromStream
,