Как связаны Span и ReadOnlySpan с хранением в стеке в C#
Что проверяют
Понимаете ли вы, что Span<T> не «хранит данные на стеке», а сам может жить только на стеке. На этом различии строится весь вопрос. Следом обычно спрашивают, что использовать в асинхронном коде.
Как отвечать
Span<T> и ReadOnlySpan<T> это ref struct, окно на непрерывный участок памяти из ссылки и длины. Данные при этом могут лежать где угодно:
- в управляемой куче:
array.AsSpan(1, 3)или срез строки черезstr.AsSpan()без копирования и аллокаций, в отличие отSubstring; - на стеке:
Span<byte> buf = stackalloc byte[256];работает безunsafe; - в неуправляемой памяти: через указатель или
NativeMemory; - в данных сборки:
ReadOnlySpan<byte>из литерала"abc"u8ничего не аллоцирует.
Почему сам span только на стеке: он может указывать внутрь объекта в куче или на stackalloc-буфер. Если бы его можно было положить в поле класса, упаковать или захватить в лямбду, он пережил бы кадр стека, на который ссылается, а сборщику пришлось бы отслеживать такие указатели из кучи. Поэтому компилятор запрещает поля Span<T> в классах, упаковку и захват в замыкание. С C# 13 ref struct разрешены локальными переменными в async-методах и итераторах, но не через границу await и yield.
Для асинхронного кода и полей есть Memory<T> и ReadOnlyMemory<T>: обычные структуры, из которых span берут через .Span в синхронном участке.
Выигрыш: разбор строк и бинарных протоколов без промежуточных массивов, отсюда перегрузки int.Parse(ReadOnlySpan<char>) и Stream.Read(Span<byte>).
Где спотыкаются
Говорят «Span хранит данные на стеке, поэтому быстрее». Span над массивом ссылается на кучу, а скорость даёт отсутствие копий, а не место хранения.
Пишут stackalloc большого или заранее неизвестного размера, тем более в цикле: память освобождается только при выходе из метода, и можно получить StackOverflowException, который не перехватывается. Для больших буферов берут ArrayPool<T>.Shared.
Пытаются вернуть span на stackalloc-буфер из метода или сохранить в поле. Компилятор это запретит, и важно уметь объяснить почему.