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;

Мои вопросы:

  1. Правильно ли я добавляю и удаляю (освобождаю) объект TStream внутри StringList? Может быть, мне нужно сначала освободить поток, а затем объект??

  2. Правильно ли я освобождаю все объекты в событии FormDestroy?

  3. Когда я пытаюсь загрузить поток обратно в 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;
  1. Я не понимаю, что вы предлагаете по поводу освобождения потока, а затем объекта. Насколько я понимаю, объект, о котором вы говорите - это поток. Вы не можете уничтожить один раньше другого, потому что есть только один объект, который является потоком.

  2. Ваши методы добавления и удаления потоковых объектов в списке строк хороши. Они не идеальны, но я ограничу свои комментарии здесь, потому что переполнение стека не является Code Review.

  3. После звонка SaveToStream, позиция потока находится в конце потока. Если вы хотите читать из потока, вам придется снова установить позицию на начало. Задавать Position := 0 для потока до вызова LoadFromStream,

Другие вопросы по тегам