为何Span<T>不可作为嵌套局部变量?嵌套方法用Span报错原因
嘿,这两个问题都问到了C#里ref-like类型(比如Span<T>)的核心设计约束,咱们一个个拆解清楚:
问题1:为什么Span<T>不能作为嵌套局部变量使用?
要搞懂这个限制,得先抓住Span<T>的本质:它是一个ref-like类型,专门用来直接操作内存(栈内存、堆内存或者非托管内存),而且它的生命周期完全绑定在当前栈帧上——一旦栈帧被销毁,Span<T>指向的内存就可能被回收或覆盖。
如果允许嵌套局部变量引用外层的Span<T>,就会埋下严重的内存安全隐患:比如外层方法执行完毕,栈帧被销毁,但嵌套方法可能被作为委托返回出去,后续调用时,嵌套方法里的Span<T>就会指向已经失效的内存,导致不可预测的内存访问错误。
C#编译器和CLR从编译阶段就直接禁止这种风险,所以干脆不允许Span<T>被嵌套局部变量捕获。
问题2:嵌套方法TryParseS使用外层Span为何报CS8175错误?
你说得对,TryParseS是嵌套方法,不是报错信息里列举的匿名方法/Lambda,但这里的关键是:C#对ref-like类型的捕获限制,覆盖了所有能捕获外层变量的嵌套可调用成员,嵌套方法也在这个范围内。
报错信息里的列举只是常见场景,不是全部。嵌套方法同样会捕获外层的局部变量(比如这里的text),而一旦Span<T>被捕获,就可能出现生命周期不匹配的问题:比如如果TestNestedSpan把TryParseS作为委托返回给外部,后续调用这个委托时,外层的text对应的栈内存已经被销毁,Span就成了悬挂引用。
而用ref ReadOnlySpan<char>传递text就没问题,因为这是显式的ref传递,CLR能确保在调用TryParseS的整个过程中,外层的text仍然处于有效的生命周期内,从根源上避免了悬挂引用的风险。
内容的提问来源于stack exchange,提问作者jyoung

