Equals:內容相等 vs 參考相等
TStringBuilder.Equals 用於判斷兩個 StringBuilder 實例的內容是否相同,而非參考相等。這與 .NET StringBuilder.Equals 行為一致:比較緩衝區實際字元內容。
核心 API
| 方法 | 參數 | 回傳 | 說明 |
Equals | obj: TObject | Boolean | 比較內容相等性 (覆寫 TObject.Equals) |
完整實作程式碼
procedure TForm1.Button1Click(Sender: TObject); // 內容比較
var
sb1, sb2: TStringBuilder;
b: Boolean;
begin
sb1 := TStringBuilder.Create('abc');
sb2 := TStringBuilder.Create('abc');
try
b := sb1.Equals(sb2); // True - 內容相同
ShowMessage(BoolToStr(b, True));
sb2.Length := 0; // 清空 sb2
b := sb1.Equals(sb2); // False - 內容不同
ShowMessage(BoolToStr(b, True));
finally
sb1.Free;
sb2.Free;
end;
end;
procedure TForm1.Button2Click(Sender: TObject); // 參考行為示範
var
sb1, sb2: TStringBuilder;
begin
sb1 := TStringBuilder.Create('abc');
try
sb2 := sb1; // sb2 指向同一實例 (參考複製)
sb1.Append('123');
ShowMessage(sb2.ToString); // 'abc123' - sb2 看到變更
sb2 := sb1.Replace('123', ' 123'); // Replace 回傳自我參考
ShowMessage(sb1.ToString); // 'abc 123'
ShowMessage(sb2.ToString); // 'abc 123' - 同一實例
ShowMessage(BoolToStr(sb2.Equals(sb1), True)); // True - 同一實例必相等
finally
sb1.Free; // sb2 同一實例,勿重複 Free
end;
end;
關鍵概念釐清
1. 內容相等 vs 參考相等
| 比較方式 | 程式碼 | 比較對象 | 結果 |
| 內容相等 | sb1.Equals(sb2) | 緩衝區字元序列 | 內容相同則 True |
| 參考相等 | sb1 = sb2 | 物件記憶體位址 | 同一實例則 True |
| 介面比較 | IInterface(sb1) = IInterface(sb2) | 介面指標 | 同一實例則 True |
2. TStringBuilder 的可變性與參考共享
TStringBuilder 是類別 (Class),屬參考型別
sb2 := sb1 只是複製參考,指向同一緩衝區
- 任何透過
sb1 或 sb2 的修改 (Append/Replace/Insert/Remove) 都會影響同一實例
Replace 等修改方法回傳 Self,方便鏈式呼叫:sb.Append('a').Replace('a','b')
3. Equals 實作邏輯 (簡化版)
function TStringBuilder.Equals(obj: TObject): Boolean;
var other: TStringBuilder;
begin
if not (obj is TStringBuilder) then Exit(False);
other := TStringBuilder(obj);
if Self = other then Exit(True); // 同一實例快速返回
if Length <> other.Length then Exit(False);
// 逐字元比較緩衝區內容
Result := CompareMem(Pointer(FData), Pointer(other.FData), Length * SizeOf(Char));
end;
實務應用場景
1. 快取鍵值比較:以內容為鍵
type
TCacheKey = record
SB: TStringBuilder;
end;
function CacheKeyEquals(const K1, K2: TCacheKey): Boolean;
begin
Result := K1.SB.Equals(K2.SB); // 內容相同視為同一鍵
end;
2. 避免重複建構相同內容
var
Cache: TDictionary; // 需自訂比較器
function GetOrBuildData(const Template: string): TData;
var
key: TStringBuilder;
existing: TStringBuilder;
begin
key := TStringBuilder.Create(Template);
try
// 需實作 IEqualityComparer 使用 Equals
if Cache.TryGetValue(key, Result) then Exit;
Result := BuildData(Template);
Cache.Add(key, Result); // key 內容作為鍵
finally
key.Free;
end;
end;
3. 除錯/測試驗證:確認操作結果
procedure TestStringBuilderOperations;
var sb1, sb2: TStringBuilder;
begin
sb1 := TStringBuilder.Create;
sb2 := TStringBuilder.Create;
try
sb1.Append('Hello').Append(' ').Append('World');
sb2.Append('Hello World');
Assert(sb1.Equals(sb2), '鏈式 Append 應等同單次 Append');
sb1.Clear.Append('Test');
sb2.Clear.Append('Test');
Assert(sb1.Equals(sb2), 'Clear 後相同內容應相等');
finally
sb1.Free; sb2.Free;
end;
end;
常見誤區與避坑
| 誤區 | 正確認知 | 程式碼示範 |
| Equals 比較參考 | 比較內容 | sb1.Equals(sb2) ≠ (sb1 = sb2) |
| Replace 產生新實例 | 修改原實例並回傳 Self | sb2 := sb1.Replace(...) → sb2 = sb1 |
| sb2 := sb1 後 Free 兩次 | 同一實例只能 Free 一次 | sb1.Free; // sb2 不可再 Free |
| Equals(nil) 會崩潰 | 內部檢查 is TStringBuilder 會回傳 False | sb1.Equals(nil) → False |
效能考量
- Equals 為 O(n):需逐字元比較,長字串較耗時
- 長度優先檢查:長度不同立即返回 False,避免內容比較
- 同一實例快速路徑:
Self = other 直接返回 True
- 大量比較場景:考慮預計算雜湊值 (
GetHashCode) 配合字典使用
總結
TStringBuilder.Equals 實作內容語意相等,區別於參考相等。理解可變參考型別特性:sb2 := sb1 共享實例,修改方法回傳 Self 方便鏈式呼叫。善用 Equals 進行內容驗證、快取鍵值比較、測試斷言,避免參考共享導致的邏輯錯誤 (如重複 Free)。