readonlyspan 是 .net 中唯一真正零分配字符串切片的方式,因其为栈上结构体、仅存起始地址和长度;误用 tostring()、跨作用域传递或错误 slice 会使其性能优势归零。

ReadOnlySpanToString()、不跨作用域传递、不误用 Slice 参数,否则性能优势瞬间归零。
为什么 ReadOnlySpan 切片不分配内存
因为 ReadOnlySpan<char></char> 本质是栈上结构体,只存原始字符串的起始地址和长度,不复制字符数据。而 string.Substring() 每次都 new 一个新 string 对象,走堆分配 —— 10 万次日志字段提取,Substring 可能多出几 MB 堆压力,ReadOnlySpan 基本无 GC 开销。
常见错误场景:
- 把
StringBuilder.ToString().AsSpan()当作零成本切片 ——ToString()已分配,再AsSpan()也晚了 - 写
new ReadOnlySpan<char>(someString.ToCharArray())</char>—— 数组分配 + 字符复制,双重浪费 - 在函数里第一行就调
line.ToString()—— 前面所有优化白做
如何安全创建和传参 ReadOnlySpan
源头决定安全性。只有从以下来源创建的 ReadOnlySpan<char></char> 才真正零成本:
-
"key=value".AsSpan()(字符串字面量) -
array.AsSpan()(托管数组,生命周期可控) -
stackalloc char[128]后转ReadOnlySpan<char></char>(栈上分配,需确保长度可控)
传参时务必注意:
- 函数签名必须声明为
ReadOnlySpan<char></char>,不能是string—— 否则一进方法就隐式调ToString() - 已支持
ReadOnlySpan<char></char>的 BCL 方法要优先用,比如:int.TryParse(ReadOnlySpan<char>, out int)</char>、string.Equals(ReadOnlySpan<char>, string, StringComparison)</char> - 不要试图把它存为类字段或跨
async方法传递 ——ref struct会被编译器直接报错
Slice 和范围语法的常见陷阱
Slice(start, length) 容易被当成 [start..end) 用,但它取的是“从 start 开始的 length 个字符”,不是以 end 为界:
-
input.Slice(3, 5)→ 索引 3、4、5、6、7 共 5 个字符(不是 [3..5)) -
input[3..8](C# 8+)语义等价,更直观,底层仍是Slice - 越界检查在 Release 模式下默认关闭,
start + length > input.Length会直接抛System.IndexOutOfRangeException - 若
start或length来自外部(如解析位置),务必先截断:var safeLen = Math.Min(length, input.Length - start),别依赖try/catch
实际解析场景:CSV 行拆分示例
下面这个函数全程零堆分配(除最后的 ToArray() 外):
static ReadOnlySpan<char>[] ParseCsvLine(ReadOnlySpan<char> line)
{
var parts = new List<readonlyspan>>();
int start = 0;
int i = 0;
while (i <p>关键限制:</p>
<ul>
<li>不处理引号包裹或转义(真实 CSV 应用 <code>Microsoft.VisualBasic.FileIO.TextFieldParser</code> 或专用库)</li>
<li>返回的 <code>ReadOnlySpan<char>[]</char></code> 中每个元素仍绑定原始 <code>line</code> 生命周期,不可长期缓存</li>
<li>若后续需传给老 API(如 <code>int.TryParse(string, ...)</code>),<code>ToString()</code> 这一步无法避免,会触发分配</li>
</ul>
<p>最易被忽略的一点:<code>ReadOnlySpan<char></char></code> 的“零分配”优势只在整条处理链路中成立;只要中间任意一环转成 <code>string</code>,或生命周期失控,它就退化成普通字符串切片,毫无特别之处。</p></readonlyspan></char></char>











