Span<T> это ref struct. А ref struct не может существовать в куче. Это не ограничение реализации, это гарантия безопасности по дизайну.Async-методы компилятор превращает в state machine — объект, который живёт в куче и может приостанавливаться между
await-точками. Локальные переменные такого метода становятся полями этого объекта. Поле типа ref struct в объекте на куче — запрещено. Поэтому компилятор просто не даст использовать Span<T> в async-методе.// Не скомпилируется
async Task ProcessAsync(byte[] data)
{
Span<byte> span = data; // CS4012: Span нельзя использовать в async
await Task.Delay(100);
Process(span);
}
Что происходит под капотом
Компилятор превращает async-метод примерно в это:
// Упрощённо — что генерирует компилятор
private struct ProcessAsyncStateMachine : IAsyncStateMachine
{
public byte[] data;
public Span<byte> span; // ← невозможно: ref struct не может быть полем
public int _state;
// ...
}
Стек фрейм между
await не гарантирован, потому что поток может смениться, метод может возобновиться на другом потоке. Span на стеке к тому моменту уже не существует.Как работать с данными в async-коде
Memory<T> — это то, для чего он и создан. Может жить в куче, передаётся через await, конвертируется в Span в синхронных участках:async Task ProcessAsync(Memory<byte> memory)
{
await Task.Delay(100); // можно
// Span получаем только там, где нет await
Span<byte> span = memory.Span;
Process(span);
}
Паттерн:
Memory<T> для хранения и передачи через async-границы, Span<T> для фактической работы с данными в синхронном контексте.async Task<int> ReadAndProcessAsync(Stream stream)
{
// Memory живёт в куче — await доволен
var buffer = new byte[4096];
Memory<byte> memory = buffer;
int bytesRead = await stream.ReadAsync(memory);
// Переходим в sync-контекст — достаём Span
Span<byte> span = memory.Span[..bytesRead];
return CountNewlines(span);
}
static int CountNewlines(Span<byte> data)
{
int count = 0;
foreach (var b in data)
if (b == '\n') count++;
return count;
}
Коротко
Span<T> — инструмент для горячего пути в синхронном коде. Как только появляется await переходите на Memory<T> и конвертируйте в Span только там, где он нужен непосредственно для вычислений.📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#il_люминатор