Khác biệt cốt lõi
| Đặc điểm | String | StringBuilder |
|---|---|---|
| Tính chất | Immutable (bất biến) | Mutable (có thể thay đổi) |
| Cơ chế | Mỗi lần sửa → tạo object mới | Sửa tại chỗ trong buffer |
| Bộ nhớ | Tạo nhiều object rác (GC pressure) | Một buffer, mở rộng khi cần |
| Hiệu năng concat | O(n²) cho loop | O(n) amortized |
| Thread-safe | Có (immutable) | Không |
Minh họa sự khác biệt
using System;
using System.Text;
class Demo
{
static void Main()
{
// String - Immutable
string s = "Hello";
StringConcat(s);
Console.WriteLine($"String: {s}"); // "Hello" - KHÔNG thay đổi!
// StringBuilder - Mutable
var sb = new StringBuilder("Hello");
StringBuilderConcat(sb);
Console.WriteLine($"StringBuilder: {sb}"); // "Hello World" - THAY ĐỔI!
}
static void StringConcat(string s)
{
s = string.Concat(s, " World"); // Tạo string MỚI, gán cho local variable s
}
static void StringBuilderConcat(StringBuilder sb)
{
sb.Append(" World"); // Sửa trực tiếp buffer nội bộ
}
}
Kết quả
String: Hello StringBuilder: Hello World
Khi nào dùng String?
- Chuỗi không thay đổi sau khi tạo.
- Số lượng concatenation ít (≤ 5-10).
- Cần thread safety.
- Key trong Dictionary, switch expression, pattern matching.
- Interpolation:
$"{a} {b}"(compiler tối ưu thànhstring.ConcathoặcStringBuilder).
Khi nào dùng StringBuilder?
- Concatenation trong loop hoặc nhiều lần.
- Xây dựng chuỗi lớn (HTML, JSON, SQL, log).
- Cần
Insert,Remove,Replacethường xuyên. - Performance critical path.
Benchmark So sánh
using System.Diagnostics;
using System.Text;
var iterations = 10000;
// String concat in loop - CHẬM
var sw1 = Stopwatch.StartNew();
string s = "";
for (int i = 0; i < iterations; i++)
{
s += "x"; // Tạo string mới mỗi lần
}
sw1.Stop();
// StringBuilder - NHANH
var sw2 = Stopwatch.StartNew();
var sb = new StringBuilder();
for (int i = 0; i < iterations; i++)
{
sb.Append("x");
}
string result = sb.ToString();
sw2.Stop();
Console.WriteLine($"String += : {sw1.ElapsedMilliseconds} ms");
Console.WriteLine($"StringBuilder: {sw2.ElapsedMilliseconds} ms");
// Kết quả điển hình: String ~2000ms, StringBuilder ~2ms
StringBuilder Best Practices
// 1. Khởi tạo với capacity ước lượng (tránh resize)
var sb = new StringBuilder(estimatedLength * 10); // hoặc 256 default
// 2. Append overloads hiệu quả
sb.Append("text");
sb.AppendLine("line");
sb.AppendFormat("{0:C}", price);
sb.AppendJoin(", ", items); // .NET Core 2.0+
// 3. Clear và reuse (tránh alloc mới)
sb.Clear();
sb.Append("new content");
// 4. ToString() không copy (trừ khi cần modify sau)
string result = sb.ToString(); // O(1) - trả về string từ buffer nội bộ (copy-on-write)
String.Interpolation & Compiler Optimization
// C# 10+: InterpolatedStringHandler - compiler tự tối ưu
public void Log(string message, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("")]
ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler handler) { }
// Sử dụng bình thường - compiler chọn StringBuilder nếu cần
Log($"User {user.Id} performed {action} at {DateTime.Now}");
// string.Concat vs StringBuilder
// 2-4 args: string.Concat nhanh nhất
// 5+ args hoặc loop: StringBuilder
Span/ArrayPool - Hiệu năng cực đại (.NET Core 2.1+)
using System.Buffers;
public string BuildLargeString(IEnumerable parts)
{
// Thuê buffer từ pool
var buffer = ArrayPool.Shared.Rent(1024);
try
{
var span = buffer.AsSpan();
int pos = 0;
foreach (var part in parts)
{
part.CopyTo(span[pos..]);
pos += part.Length;
}
return new string(span[..pos]);
}
finally
{
ArrayPool.Shared.Return(buffer);
}
}
Tóm tắt quyết định
Quy tắc: Mặc định dùng
string / interpolation. Chỉ dùng StringBuilder khi: (1) Loop concat, (2) Nhiều thao tác sửa đổi, (3) Benchmark chứng minh bottleneck.