🧩 string 是引用类型,却“像值类型”?
在 C# 中,string 属于 引用类型(System.String),但它具备值语义:你“修改”字符串时,并不会改动原对象,而是创建一个新的字符串实例。这就是 不可变(Immutable) 的含义,也是很多人觉得它“像值类型”的原因。
✨ 一个直观例子
string a = "hello";
string b = a;
b = "world";
Console.WriteLine(a); // hello
Console.WriteLine(b); // world由于不可变,
b重新指向了一个新的"world"实例,而a仍然指向原来的"hello"实例。
⚠️ 频繁拼接的性能隐患
当你在循环里用 + 或插值拼接字符串时,每一次“修改”都会分配新对象,带来多余的内存分配和 GC 压力。在日志构建、HTML 片段拼接、长文本生成等场景,性能会明显下降。
🚀 更高效的选择:StringBuilder
StringBuilder 内部维护可扩展缓冲区,就地修改、减少对象创建,适合大量/频繁拼接的场景。
using System.Text;
var sb = new StringBuilder();
sb.Append("Hello");
sb.Append(", ");
sb.Append("World!");
sb.Append(" 👋");
string result = sb.ToString(); // "Hello, World! 👋"🔁 循环场景对比
// ❌ 低效:每次拼接都创建新字符串
string s = string.Empty;
for (int i = 0; i < 1000; i++)
{
s += i + ",";
}
// ✅ 高效:缓冲区就地扩展
var sb2 = new StringBuilder(capacity: 4096); // 预估容量可进一步减少扩容
for (int i = 0; i < 1000; i++)
{
_ = sb2.Append(i).Append(',');
}
string s2 = sb2.ToString();🧭 选择建议
- ✅ 少量拼接/一次性生成:直接用
string或字符串插值(可读性最好)。 - ✅ 大量/循环拼接:优先
StringBuilder(可预估容量时设置capacity)。 - ✅ 格式化复杂模板:
string.Create(高级用法)或StringBuilder.AppendFormat。 - ✅ 不可变带来的好处:线程安全(引用层面)、易缓存、易比较(驻留/Intern 可能生效)。
📌 小结对照表
| 场景/特性 | string(不可变) | StringBuilder(可变) |
|---|---|---|
| 类型 | 引用类型(值语义表现) | 引用类型(缓冲区可变) |
| 修改开销 | 创建新对象 | 就地修改,少分配 |
| 适用场景 | 少量拼接、常量、配置、键名 | 大量拼接、循环生成、日志 |
| 可读性 | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| 性能(大量拼接) | ⭐ | ⭐⭐⭐⭐ |
🗣 一句话记忆
小量用
string,大量用StringBuilder;一次性用插值,循环里用缓冲。
🔚 结语
理解 string 的不可变性,你就理解了 C# 字符串的内存与性能模型。
在合适的场景使用 StringBuilder,能显著减少分配、提升吞吐,让你的 .NET 服务更稳更快。
© MUZINET · 有客赞 | 分享 .NET / C# 与现代架构实践
最后更新于