该用StringBuilder的场景是高频追加、长度不可控、最终才需字符串;拼接少于4次且含常量时用+或$""更优,编译器会优化为string.Concat;循环内拼接列表项等动态长内容才真正需要StringBuilder。

别在循环里用 + 拼接再塞进 StringBuilder,也别一上来就给所有字符串拼接都套 StringBuilder——它只在“高频追加、长度不可控、最终才需字符串”的场景下真正提效。
什么时候该 new StringBuilder(),什么时候直接用 + 或 $""
拼接次数少于 4 次、内容全为常量或已知短字符串(比如 "User: " + name + ", ID: " + id),编译器会自动优化成 string.Concat,比 StringBuilder 还快。硬上反而多建对象、多一次方法调用。
真该用的典型场景是:
- 循环内拼接列表项(日志批量组装、CSV 行生成、SQL
IN子句) - 单次拼接内容动态且可能超几百字符(如模板渲染、XML 片段拼装)
- 拼接次数完全不可预知(流式读取+累积处理)
常见翻车:写 for (int i = 0; i ,跑着跑着 GC 告警——这时候才是 StringBuilder 的入场时机。
初始化必须预估容量,否则白搭
默认构造函数 new StringBuilder() 初始容量只有 16,一旦超出就触发扩容(约 1.5 倍增长),每次扩容都要 Array.Copy 整个内部 char[],代价不小。
正确做法是初始化时传入合理预估长度:
- 拼 CSV:按每行平均字符数 × 行数估算
- 拼 JSON 片段:参考字段名+值长度+逗号/引号等固定开销
- 拿不准?宁可高估 20%(比如预估 800 → 设
new StringBuilder(1000)),也别让它扩三次
别依赖 Capacity 属性后期调整——设置它会强制 realloc;更别用 EnsureCapacity 在循环里反复调,那等于主动制造抖动。
Append() 调用链必须“原子化”,禁止中间掺杂 + 或 ToString()
错误写法:sb.Append("ID=" + id + ", Name=" + name) —— 这行代码先用 + 拼出两个新字符串,再把结果喂给 Append(),StringBuilder 白建了。
正确写法是拆成独立 Append() 调用:
sb.Append("ID=").Append(id).Append(", Name=").Append(name);
额外注意:
- 拼数字/布尔/枚举,直接
Append(123)、Append(true),别Append(id.ToString())——重载方法走无分配路径,省堆内存 -
AppendLine()比Append("\n")更安全(自动适配 \r\n 或 \n),但别滥用:如果目标格式严格要求 Unix 换行,就用Append("\n") -
ToString()只在最终输出前调一次,比如写文件、返回 API、传给正则引擎;调试看内容用sb.Length和sb.ToString().Substring(0, 100)(截断防卡死)
别把它当 string 使:Replace()、Remove()、Insert() 是性能黑洞
Replace() 和 Remove() 在 StringBuilder 里不是 O(1),它们要扫描、搬移、重建缓冲区。一旦出现,说明设计反了。
典型误用:
sb.Append(line);
sb.Replace("\t", " "); // 错!格式化不该放这里
应改为:
- 原始数据预处理:读取时就用
line.Replace("\t", " ")得到干净字符串,再Append() - 批量替换需求,用
Regex.Replace()预处理整个源集合,再拼接 - 真要删某段,
sb.Remove(start, length)比循环DeleteCharAt()强,但仍应尽量避免——多数情况是逻辑该重构,而不是靠Remove补救
最易被忽略的一点:Clear() 不释放底层数组,只重置 Length;复用时务必确认线程安全——它不是线程安全类型,跨线程共享等于埋雷。











