字符串插值需严格满足$前缀和{}包裹变量两大条件,否则引发编译错误或运行时异常;格式符须紧贴}、避免空格;null安全需括号包裹;性能敏感场景应优先用stringbuilder;跨平台需显式指定cultureinfo。

字符串插值不是加个 $ 就能安全用好的语法糖,它在 C# 6.0+ 中可用,但写错、用重、跨平台不一致这三类问题最常导致运行时异常或本地调试正常而线上出错。
插值语法必须同时满足两个硬性条件
缺一不可:字符串前必须有 $,且所有变量/表达式必须严格包裹在 {} 内。编译器不会“猜意图”。
- 漏
$:写成"Hello {name}"→ 编译失败,报CS1056: Unexpected character '$'(如果误加了 $ 却没配 {})或直接当普通字符串处理 - 漏
{}:写成$"Hello name"→ 输出字面量Hello name,name变量完全不参与替换 - 嵌套引号未转义:
$"Name: \"{name}\""或更稳妥的逐字插值$@"Name: ""{name}""",否则会解析失败
格式化符号必须紧贴右大括号,中间不能有空格
这是高频踩坑点:格式说明符(如 :C2、:yyyy-MM-dd)必须直接跟在表达式后、} 前,任何空格都会让编译器忽略该格式,退化为调用默认 ToString()。
- ✅ 正确:
$"{price:C2}"、$"{dt:HH:mm:ss}"、$"{value,8:N0}"(宽度+数字格式) - ❌ 错误:
$"{price: C2}"(空格导致:C2被丢弃)、$"{user.Name ?? "N/A":G}"(??表达式后不能直接接格式符) - 对 null 的安全格式化需拆开:
$"{(user?.Name ?? "Anonymous"):G}",括号保证表达式整体可被格式化
性能敏感场景下,插值不是万能解法
每次使用 $"..." 都隐式调用 string.Format 或构造新字符串对象。在循环内高频拼接,GC 压力会明显上升。
- 日志批量写入、网络包组装、大量 CSV 行生成等场景,优先用
StringBuilder:sb.Append($"ID:{id}, Name:{name}") - 避免在插值中调用耗时方法:
$"Result: {ExpensiveCalculation()}"→ 改为先赋值再插值:var result = ExpensiveCalculation(); $"Result: {result}" - 全字面量拼接(如
$"v1.2.3-{DateTime.Now:yyyyMMdd}")可被编译器优化,但含变量即失去常量优化机会
跨平台输出不一致?文化差异是默认行为,不是 bug
$"{1234.56:C}" 在 en-US 环境输出 $1,234.56,在 de-DE 输出 1.234,56 € —— 这是设计使然,不是错误。API 返回 JSON、文件名生成、数据库查询拼接等需要确定性格式的场合,必须显式控制文化。
- 强制不变文化:
$"{value:C2}".ToString(CultureInfo.InvariantCulture)不行(插值已完成),应改用string.Format(CultureInfo.InvariantCulture, "{0:C2}", value) - 或提前格式化:
var formatted = value.ToString("C2", CultureInfo.InvariantCulture); $"Price: {formatted}" - 自定义类型要支持插值格式化,必须实现
IFormattable,仅重载ToString()不足以响应:G、:X等格式符
真正难的不是记住 $ 和 {},而是判断什么时候不该用插值——比如表达式嵌套三层、循环里拼 10 万次、或需要和前端约定死小数点分隔符时,硬套 $"..." 只会让问题更隐蔽。











