У меня есть ushort[] то есть, например, Длина 7,
var ushorts = new ushort[7] {1, 2, 3, 4, 5, 6, 7};
это 14 последовательных байтов в памяти.
01 00 02 00 03 00 04 00 05 00 06 00 07 00
Мне нужен byte[] где-то в середине этих 14 байт, без копирования данных.
Вот пример того, как это делается на C++:
#include <iostream>
typedef unsigned short ushort;
typedef unsigned char byte;
int main() {
ushort* ushorts = new ushort[7] {1, 2, 3, 4, 5, 6, 7};
byte* bytes = reinterpret_cast<byte*>(&(ushorts[2]));
for (int i = 0; i < 8; i++) {
std::cout << (int)bytes[i] << ' ';
}
// stdout: 3 0 4 0 5 0 6 0
std::cout << std::endl;
delete ushorts;
return 0;
}
В C#, как я могу получить byte[] из произвольного места в ushort[] без копирования данных?
Строго как byte[] нельзя, а вот с пролётами можно. Исследуйте Span<T> и ReadOnlySpan<T>.
Вы сказали «Без копирования данных». Не могли бы вы уточнить, имеете ли вы в виду «без копирования каких-либо данных» или «без копирования всех данных»? Если вы согласны избегать копирования всего массива, но копируете интересующие данные из середины, то Buffer.BlockCopy.
без копирования каких-либо данных, так же как в примере C++ bytes нет копии данных
Как вы собираетесь его использовать? Если вы смотрите на это непосредственно (как это делает ваш примерный цикл), промежутки будут работать отлично. Если вам нужно передать его какому-то методу, который ожидает массив, вам нужно будет сделать копию. В BCL были добавлены перегрузки, которые принимают интервалы в дополнение к тем, которые принимают массивы. Сторонние библиотеки будут работать как попало. Пожалуйста, расскажите, как вы будете использовать это окно в исходном массиве, так как это покажет, возможно ли решение без копирования.
Я мог бы использовать Span<byte>, но я немного изучил Span и не нашел способа получить байты базовых структур данных. Я мог бы принять ответ, чтобы показать, как это сделать.
Span<T> определяет оператор [] (в терминах C++, в терминах C# это не свойство индексатора), поэтому нормальное индексирование возможно в цикле for. Он также поддерживает IEnumerable<T>, так что вы можете использовать foreach.
Да, я все это видел. Но я не знаю, как получить отдельные байты для структур данных, состоящих более чем из одного байта. Можете ли вы перевести пример C++ так, чтобы bytes был Span<byte> и давал тот же результат?
«структуры данных больше одного байта» — это новый вопрос. Этот вопрос слишком минимален, чтобы ответить на него. Что вы пытаетесь десериализовать?
MemoryMarshal.Cast это то, чего мне не хватало. Я бы предположил, что он сохранил то же количество элементов вместо того же количества байтов.
ushort — это структура данных, состоящая более чем из одного байта. Просто это тоже примитив. Было бы неплохо, если бы MemoryMarshal.Cast работало так же для непримитивов, но, к счастью, это сработает и в моем случае.
Вы можете использовать structs для этого. При правильном расположении вы можете интерпретировать последовательность байтов как экземпляр структуры.





Вы не можете получить массив середины, но вы можете получить диапазон (Span<byte> или ReadOnlySpan<byte>):
ushort[] original = ...
var bytes = MemoryMarshal.AsBytes(original); // zero-copy
var slice = bytes.Slice(4, 7); // zero copy
Следите за порядком байтов процессора при работе с операциями от ushort до byte (это в равной степени относится и к версии C/C++).
Обратите внимание, что есть также MemoryMarshal.Cast для сценариев, которые не используют байты — например, переход от Span<int> к Span<Vector<int>> для целей SIMD.
Я пытался возиться с этим внедрением reinterpret_cast для C# stackoverflow.com/a/42078952/3261120, но я не знаю, как получить середину массива, и, как указывает автор, это не работает для вещи разных размеров.