Как я могу получить Span <t> из списка <t> избегая ненужных копий?

У меня есть List<T> содержащий некоторые данные. Я хотел бы передать его функции, которая принимает ReadOnlySpan<T>.

List<T> items = GetListOfItems();
// ...
void Consume<T>(ReadOnlySpan<T> buffer)
// ...
Consume(items??);

В этом конкретном случае T является byte но это не имеет большого значения.

Я знаю, что я могу использовать .ToArray() в списке и построить span, например

Consume(new ReadOnlySpan<T>(items.ToArray()));

Однако это создает (по-видимому) ненужную копию элементов. Есть ли способ получить Span непосредственно из списка? List<T> реализуется в терминах T[] за кулисами, поэтому теоретически это возможно, но не настолько, насколько я вижу на практике?

Ответ 1

Спасибо за все комментарии, объясняющие, что нет никакого реального способа сделать это, и как разоблачение внутреннего массива внутри Списка может привести к плохому поведению и сломанному интервалу.

Я закончил рефакторинг моего кода, чтобы не использовать список и просто производить пролеты в первую очередь.

void Consume<T>(ReadOnlySpan<T> buffer)
// ...

var buffer = new T[512]; 
int itemCount = ProduceListOfItems(buffer); // produce now writes into the buffer

Consume(new ReadOnlySpan<T>(buffer, 0, itemCount);

Я пытаюсь сделать явный компромисс избыточного выделения буфера один раз, чтобы избежать дополнительной копии позже.

Я могу сделать это в моем конкретном случае, потому что я знаю, что будет максимальная верхняя граница на счету элемента, а чрезмерное распределение немного не имеет большого значения, однако здесь, похоже, не существует обобщения, и никто никогда не будет добавьте, так как это было бы опасно.

Как всегда, производительность программного обеспечения - это искусство создания (надеюсь, благоприятных) компромиссов.

Ответ 2

Вы можете написать свой собственный CustomList<T> который предоставляет базовый массив. Именно тогда пользовательский код правильно использует этот класс.

В частности, CustomList<T> не будет знать о Span<T> который вы можете получить из базового массива поддержки. После выполнения Span<T> вам не следует делать список, чтобы создать новый массив или создать неопределенные данные в старом массиве.

Стандартная библиотека C++ позволяет коду пользователя получать прямые указатели в vector<T> хранилище vector<T>. Они документируют условия, при которых это безопасно. Например, изменение размера делает его небезопасным.

Сам.NET делает это с MemoryStream. Этот класс позволяет вам получить доступ к базовому буферу и действительно опасные операции возможны.