Как я могу получить 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
. Этот класс позволяет вам получить доступ к базовому буферу и действительно опасные операции возможны.