核心区别在于可变性:Span允许读写底层内存,ReadOnlySpan仅允许读取,且编译器强制禁止对ReadOnlySpan赋值;二者共享内存管理机制但类型系统严格隔离写权限。
c#如何选择使用哪一个">
Span 和 ReadOnlySpan 的核心区别在哪
关键在于可变性:Span<t></t> 允许读写底层内存,ReadOnlySpan<t></t> 只允许读取。这不是简单的命名约定,而是编译器强制的契约——试图对 ReadOnlySpan<t></t> 调用 span[0] = value 会直接报错 CS1603: Cannot assign to a readonly variable(即使底层是可写的数组)。
二者共享同一套内存管理机制(栈分配、无 GC 压力、支持堆/栈/本机内存),但类型系统把“写权限”作为不可逾越的边界。
什么时候必须用 ReadOnlySpan
当你明确不打算修改数据,且希望向调用方或后续逻辑传递“只读承诺”时,优先选 ReadOnlySpan<t></t>。这不只是风格问题,它影响 API 设计和安全性:
- 接收字符串切片时,
ReadOnlySpan<char></char>是标准入口(如int.TryParse(ReadOnlySpan<char>, ...)</char>) - 传入只读配置数据、解析协议头、校验哈希摘要等场景,用
ReadOnlySpan<byte></byte>防止误写 - 泛型方法约束中,若仅需遍历或查找,用
in ReadOnlySpan<t></t>可避免不必要的复制,且比in Span<t></t>更安全
什么情况下只能用 Span
需要原地修改内容时别无选择。典型场景包括:
- 缓冲区填充:从网络流读取后直接写入
Span<byte></byte>并解析 - 就地格式化:如把整数转为 ASCII 字符串写入预分配的
Span<char></char> - 算法实现:快速排序、反转、大小写转换等需写操作的逻辑
注意:Span<t></t> 不能作为类字段、不能跨 await 边界、不能存储在堆对象中——这些限制对 ReadOnlySpan<t></t> 同样适用。二者都受限于“栈语义”的生命周期规则。
参数传递和性能差异怎么选
没有运行时性能差别。二者都是 ref-like 类型,传递开销极小(本质是两个字段:指针 + 长度)。选择依据纯属语义和契约:
- 方法参数能用
ReadOnlySpan<t></t>就不用Span<t></t>,除非你真要改它 - 返回值尽量用
ReadOnlySpan<t></t>(比如string.AsSpan()返回的就是ReadOnlySpan<char></char>) - 若上游只提供
Span<t></t>,但你只读,可用显式转换:ReadOnlySpan<t> ro = span;</t>(安全,零成本);反过来则不允许隐式转换,必须确认所有权并使用span.ToArray().AsSpan()等代价更高的方式
最容易被忽略的是:即便数据来源是可变数组,只要逻辑上不需要写,就该用 ReadOnlySpan<t></t> —— 这不是过度设计,而是让意图清晰、避免未来误改、也方便编译器做更多优化判断。










