TStream 作为 StringList 中的一个对象

TStream as an object inside StringList

我正在使用 Delphi 7 并使用 StringList,以 TStream 作为对象。

我的测试项目有一个列表框、一个备忘录和 2 个按钮(添加和删除)。

这是我目前得到的结果:

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. 我在 StringList 中添加和删除(释放)TStream 对象的方式是否正确?也许我需要先释放 Stream,然后再释放 Object??

  2. 我在 FormDestroy 事件中释放所有对象的方式是否正确?

  3. 当我尝试将流加载回 Memo (Memo.Lines.LoadFromStream(TStream(List.Objects[Idx]))) 时,尽管 Stream.Size 高于零。我做错了什么?

  1. 我不明白你关于释放流然后释放对象的建议。据我了解,您正在谈论释放 的对象是 流。你不能先销毁一个,因为只有一个对象,它是一个流。

  2. 您在字符串列表中添加和删除流对象的方法没问题。它们并不理想,但我会在这里限制我的评论,因为 Stack Overflow 不是 Code Review.

  3. 调用SaveToStream后,流的位置在流的末尾。如果你想从流中读取,那么你必须再次将位置设置回开始。在调用 LoadFromStream.

  4. 之前为流设置 Position := 0

1.Is correct the way I am adding and removing (freeing) the TStream object inside the StringList?

是的,因为 TStrings.Objects[] 属性 returns 一个 TObject 指针和 TStream 派生自 TObject,所以你可以调用 Free() 在对象指针上。

Maybe I need to first free the Stream and then the Object??

您需要在释放 TStringList 对象之前释放 TStream 对象。正如您已经在做的那样。

2.Is correct the way I am freeing all objects on FormDestroy event?

是的。尽管从技术上讲,您不需要在进入循环之前检查 TStringList.Count 属性 是否为 > 0,因为循环会为您处理该情况。在释放 TStringList:

之前,您不需要 nil 指针
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 TListBox add/delete 来自 TStringList 的单个项目。您应该改为 add/delete 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.When I try to load the stream back to Memo (Memo.Lines.LoadFromStream(TStream(List.Objects[Idx]))), it doesn't load, despite Stream.Size is higher than zero. What I am doing wrong?

在将流加载到备忘录中之前,您没有将流返回到 Position0。 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;