3

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

我的测试项目有一个 ListBox、一个备忘录和 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 对象加载到备忘录。这是我试图做的:

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 对象的方式是否正确?也许我需要先释放流然后释放对象?

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

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

4

2 回答 2

3
  1. 我不明白您对释放流然后释放对象的建议。据我了解,您所说的释放对象流。你不能在另一个之前销毁一个,因为只有一个对象,它是一个流。

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

  3. 调用后SaveToStream,流的位置在流的末尾。如果要从流中读取,则必须再次将位置设置回开始。Position := 0在调用之前为流设置LoadFromStream

于 2015-07-20T22:01:17.253 回答
3

1.我在StringList中添加和删除(释放)TStream对象的方式是否正确?

是的,因为该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()每当 TStringListTListBoxTStringList. 相反,您应该简单地从 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.Lines.LoadFromStream(TStream(List.Objects[Idx]))) 时,它不会加载,尽管 Stream.Size 高于零。我做错了什么?

Position在将流加载到备忘录之前, 您不会将流返回到0。SaveToStream()始终将流置于流的末尾,并将LoadFromStream()流置于负载停止读取的任何位置(如果不在末尾,以防发生故障)。

现在,说了这么多,我个人不会TListBox以这种方式使用。相反,我会将其Style属性设置为lbVirtual,然后使用其OnData事件来显示来自TStringList. 无需将它们TListBox直接复制到其中,或尝试始终使两个列表保持同步。它会更安全,使用更少的内存,让TListBox询问你它需要什么,然后你可以从提供它TStringList(然后我会改为 a,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;
于 2015-07-20T22:32:01.083 回答