Как получить `byte[]` из некоторых байтов другого массива без копирования

У меня есть 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[] без копирования данных?

Я пытался возиться с этим внедрением reinterpret_cast для C# stackoverflow.com/a/42078952/3261120, но я не знаю, как получить середину массива, и, как указывает автор, это не работает для вещи разных размеров.

beauxq 02.06.2023 22:12

Строго как byte[] нельзя, а вот с пролётами можно. Исследуйте Span<T> и ReadOnlySpan<T>.

madreflection 02.06.2023 22:13

Вы сказали «Без копирования данных». Не могли бы вы уточнить, имеете ли вы в виду «без копирования каких-либо данных» или «без копирования всех данных»? Если вы согласны избегать копирования всего массива, но копируете интересующие данные из середины, то Buffer.BlockCopy.

Ben Voigt 02.06.2023 22:43

без копирования каких-либо данных, так же как в примере C++ bytes нет копии данных

beauxq 02.06.2023 22:53

Как вы собираетесь его использовать? Если вы смотрите на это непосредственно (как это делает ваш примерный цикл), промежутки будут работать отлично. Если вам нужно передать его какому-то методу, который ожидает массив, вам нужно будет сделать копию. В BCL были добавлены перегрузки, которые принимают интервалы в дополнение к тем, которые принимают массивы. Сторонние библиотеки будут работать как попало. Пожалуйста, расскажите, как вы будете использовать это окно в исходном массиве, так как это покажет, возможно ли решение без копирования.

madreflection 02.06.2023 22:58

Я мог бы использовать Span<byte>, но я немного изучил Span и не нашел способа получить байты базовых структур данных. Я мог бы принять ответ, чтобы показать, как это сделать.

beauxq 02.06.2023 23:10
Span<T> определяет оператор [] (в терминах C++, в терминах C# это не свойство индексатора), поэтому нормальное индексирование возможно в цикле for. Он также поддерживает IEnumerable<T>, так что вы можете использовать foreach.
madreflection 02.06.2023 23:11

Да, я все это видел. Но я не знаю, как получить отдельные байты для структур данных, состоящих более чем из одного байта. Можете ли вы перевести пример C++ так, чтобы bytes был Span<byte> и давал тот же результат?

beauxq 02.06.2023 23:19
dotnetfiddle.net/3atcRT
madreflection 02.06.2023 23:29

«структуры данных больше одного байта» — это новый вопрос. Этот вопрос слишком минимален, чтобы ответить на него. Что вы пытаетесь десериализовать?

madreflection 02.06.2023 23:33
MemoryMarshal.Cast это то, чего мне не хватало. Я бы предположил, что он сохранил то же количество элементов вместо того же количества байтов.
beauxq 02.06.2023 23:47
ushort — это структура данных, состоящая более чем из одного байта. Просто это тоже примитив. Было бы неплохо, если бы MemoryMarshal.Cast работало так же для непримитивов, но, к счастью, это сработает и в моем случае.
beauxq 02.06.2023 23:51

Вы можете использовать structs для этого. При правильном расположении вы можете интерпретировать последовательность байтов как экземпляр структуры.

madreflection 02.06.2023 23:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
13
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы не можете получить массив середины, но вы можете получить диапазон (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.

Другие вопросы по теме