2015-07-20 20 views
2

我使用Delphi 7並使用StringStream和TStream作爲對象。TStream作爲StringList中的一個對象

我的測試項目有一個列表框,一個備忘錄和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; 

當我雙擊列表框,我想選擇的項目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的內部的T流對象?也許我需要先釋放流,然後釋放對象?

  2. 正確的方式我釋放FormDestroy事件上的所有對象?

  3. 當我嘗試將流加載回備註(Memo.Lines.LoadFromStream(TStream(List.Objects [Idx])))時,儘管Stream.Size高於零,但它不會加載。我做錯了什麼?

+0

沒有必要投'Strm'到'TObject'在'List.AddObject(IntToStr(List.Count),TObject的(STRM)); 'line'Strm' ** IS ** a TObject –

+0

@GerryColl,謝謝你的提示! – Guybrush

+0

你的錯誤是試圖強制一個流到Objects []屬性中。創建一個持有你需要的類型。 –

回答

3

1.Is糾正我添加和刪除的方式(釋放)的StringList的內部的T流對象?

是的,因爲TStrings.Objects[]屬性返回TObject指針和TStreamTObject派生,所以你可以調用該對象的指針Free()

也許我需要先釋放流,然後是對象?

在釋放TStringList對象之前,您需要先釋放對象TStream。就像你已經在做的一樣。

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()荷蘭國際集團的整個TStringListTListBox當添加/刪除單個物品來自TStringList。您應該簡單地從列表框中添加/刪除關聯的項目並保留其餘項目。

並添加一些額外的錯誤檢查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(T流(List.Objects [IDX]))),這不是」 t負載,儘管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; 
+0

太棒了!謝謝! – Guybrush

3
  1. 我不明白你的建議有關釋放流,然後將對象是什麼。據我瞭解,你所說的釋放的對象是這個流。你不能先破壞另一個,因爲只有一個對象,這是一個流。

  2. 您在字符串列表中添加和刪除流對象的方法很好。他們不是理想的,但我會限制我的意見,因爲堆棧溢出不是Code Review

  3. 在您撥打SaveToStream之後,流的位置在流的處。如果您想從流中讀取數據,則必須將位置重新設置回開始位置。在致電LoadFromStream之前設置流的Position := 0

+0

另外請記住,將'TStringList'分配給'TListBox''也會將'TStream'對象分配給ListBox作爲列表項的關聯用戶定義值(通過'LB_SETITEMDATA'消息) 。 –

+0

@RobKennedy,關於第一個問題,我想我可能需要先釋放流(TStream(List.Objects [Idx])。Free),然後是對象本身。但現在我明白了......關於第二個問題,請給我一個關於這個理想方法的小費,請:) – Guybrush

+0

@RemyLebeau,謝謝你的提示。我將字符串列表分配給列表框僅用於測試。此代碼的用途是與TIdThreadSafeStringList在IdTCPServer通信中一起使用,因此它可以知道何時向客戶端發送流。 – Guybrush

相關問題