
String.Format 里占位符怎么写才不报错
占位符写成 {0}、{1} 这种序号形式最稳妥,索引必须从 0 开始连续,不能跳号或重复。比如 String.Format("{0} {2}", "a", "b", "c") 会抛出 IndexOutOfRangeException —— 因为只传了 3 个参数,但 {2} 是合法的(索引 2 对应第三个参数),真正出错的是 {0} {2} {1} 这种看似合理但参数数不够的情况。
常见错误现象:Input string was not in a correct format,大概率是占位符数量和参数数量对不上,或者用了命名占位符却没用 FormattableString(C# 6+)配合插值字符串,而仍用传统 String.Format。
- 不要写
{name}这类命名占位符——String.Format不支持,得用 $"{name}" - 如果参数是
null,占位符默认输出空字符串,不会崩,但要注意后续逻辑是否依赖非空 - 格式说明符要跟在冒号后面,例如
{0:C2}表示货币格式保留两位小数
C# 里 Format 和 $ 插值字符串到底该选哪个
优先用 $ 插值字符串,它本质是编译器转换为 string.Format 或更优的 string.Create(.NET Core 3.0+),可读性高且支持表达式(如 $"{user.Name?.ToUpper()}"),而 String.Format 只能传变量或字面量。
但有些场景绕不开 String.Format:
- 格式模板来自配置或数据库(动态拼接的格式串,无法用 $)
- 需要复用同一格式多次(比如日志模板),且参数顺序/数量不固定,这时把格式串抽成常量再调用
String.Format更清晰 - 要控制 IFormatProvider(如指定文化信息),
String.Format(IFormatProvider, string, params object[])比插值更直接
性能上,简单场景插值略快;但若频繁拼接且格式复杂,两者差异微乎其微,别过早优化。
日期、数字格式化时容易忽略的细节
日期用 {0:yyyy-MM-dd HH:mm:ss} 没问题,但注意 HH 是 24 小时制,hh 是 12 小时制;数字用 {0:N2} 会加千分位和两位小数,但若值为 null 或非数字类型(比如传了 DateTime 却用数字格式),运行时抛异常而不是静默失败。
-
{0:d}是短日期(如2024-05-20),{0:D}是长日期(含星期,如Monday, May 20, 2024),大小写敏感 - 自定义数字格式如
{0:000.00},不足位补零;{0:#.##}不足位不补,但也不会显示多余零 - 如果参数是
double.NaN,用N格式会输出NaN字符串,不是空——这点常被忽略,校验逻辑要覆盖
Format 的线程安全与内存分配问题
String.Format 是线程安全的,但每次调用都会分配新字符串对象。高频场景(如循环内拼日志)可能触发 GC 压力。替代方案:
- 用
StringBuilder+AppendFormat复用缓冲区,尤其当格式固定、参数变化时 - .NET 6+ 可考虑
string.Create配合Span<char></char>,避免中间字符串分配,但代码变复杂,仅热点路径值得投入 - 别为了“省一次分配”强行把多个
String.Format合并成一个大格式串——可读性和维护性下降更快
var sb = new StringBuilder();
sb.AppendFormat("User {0} logged in at {1:O}", userId, DateTime.UtcNow);
return sb.ToString();
格式化本身不难,难的是在动态性、可读性、性能之间找平衡点。多数业务代码用 $ 就够了;只有当你手里的格式串是运行时决定的,或者要精确控制文化信息时,才真正需要伸手去摸 String.Format。











