C# — конвертировать любой небезопасный неуправляемый тип в управляемый тип без нового выделения памяти — сохраняя тот же размер в памяти

Я хочу преобразовать массив ushort в короткий массив без выделения новой памяти в ядре C#.

Также можно преобразовать любой тип, например byte[] в short[] или наоборот. При преобразовании необходимо сохранить одинаковый размер памяти как для источника, так и для места назначения

В C был синтаксис «объединения», к которому тип можно отнести с несколькими приведениями типов — я не нашел эквивалента для C# (работает только для непримитивных типов).

Один из способов - кодировать так

ushort[] res = new ushort[1024 * 1024 * 1024];
short[] s = new short[1024 * 1024 * 1024];
Buffer.BlockCopy(s, 0, res, 0, s.Length * 2);

... но я не буду выделять значение 's' - это слишком много памяти и может привести к утечке памяти.

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

Код:

unsafe
{
    ushort[] res = new ushort[1024*1024*1024]; // large allocating
    fixed (ushort* ss = &res[0])
    {
        IntPtr ps = (IntPtr)ss;
        short[] s0 = (short[])Marshal.PtrToStructure(ps, typeof(short[]));
    }
}

столкнуться с исключением.

Для этого объекта не определен конструктор без параметров.

...

в System.RuntimeTypeHandle.CreateInstance (тип RuntimeType, логическое значение publicOnly, логическое значение wrapExceptions, логическое значение и canBeCached, RuntimeMethodHandleInternal и ctor)

at System.RuntimeType.CreateInstanceSlow (логическое значение publicOnly, логическое значение wrapExceptions, логическое значение skipCheckThis, логическое значение fillCache)

в System.Runtime.InteropServices.Marshal.PtrToStructure(IntPtr ptr, TypestructureType)

Как я могу в конечном итоге преобразовать этот большой массив в 1 ГБ без необходимости выделения новой памяти?

Можно ли преобразовать тип массива из неуправляемого типа в управляемый?

Я предпочитаю использовать небезопасный режим, если это вообще возможно.


После глубокого поиска я нашел решение, в котором я бы участвовал (и редактировал этот пост), но у него есть проблема, описанная ниже:

Этот код работает очень хорошо, если массивы имеют одинаковый размер. Возникает новый вопрос: как я могу решить его для разных размеров базового типа массива (т. е. short[] vs byte[])?

Единственное, я не могу конвертировать из short[] в byte[].

(Спасибо блогу в Интернете: https://social.msdn.microsoft.com/Forums/vstudio/en-US/06ac44b0-30d8-44a1-86a4-1716dc431c62/how-to-convert-an-intptr-to-an-object-in-c?forum=clr)

Это работает и на ядре С#, и на С# .NET.

Первым делом создайте класс:

public static class MyConverter
{
    public static unsafe T GetInstance<T>(IntPtr pointer)
    {
        try
        {
            var fakeInstance = default(T);
            TypedReference typedReference = __makeref(fakeInstance);
            *(IntPtr*)(&typedReference) = pointer;
            T instance = (T) __refvalue(typedReference,T);
            return instance;
        }
        catch
        {
            return default(T);
        }
    }
}

и может использовать его в таком коде:

      ushort[] x = new ushort[1024];
      GCHandle handle1 = GCHandle.Alloc(x);
      IntPtr px = (IntPtr)handle1;
      x[0] = 1;
      x[1] = 2;
      x[2] = 3;
      short[] s = MyConverter.GetInstance<short[]>(px);

Это решение преобразует ushort[] в short[] без потери памяти.

Спасибо.


person Eitan    schedule 20.01.2019    source источник
comment
Пожалуйста, не редактируйте ответ в вопросе. Воспользуйтесь разделом Ответ   -  person Suraj Rao    schedule 21.01.2019
comment
Это был не ответ (я удалил свой ответ, но он не совсем идеален. В ответе отсутствует преобразование, которое я действительно не могу сделать (байт [] ‹-> шорт [], потому что шорт и байт имеют разный размер).   -  person Eitan    schedule 21.01.2019
comment
хорошо .. я вернул его   -  person Suraj Rao    schedule 21.01.2019
comment
Спасибо. Я почти возвращаю свой удаленный ответ...   -  person Eitan    schedule 21.01.2019


Ответы (1)


Вы можете легко конвертировать так: (ushort[])(object)myShortArray. CLR это позволяет.

Это разрешено только для примитивных типов элементов массива одинакового размера.

В других случаях вы можете использовать Span для обработки памяти как другого типа.

Span<int> asInts = MemoryMarshal.Cast<byte, int>(asBytes);
person usr    schedule 21.01.2019
comment
Я не понял. Для следующего кода я не могу запустить SetArr. Как я уже сказал - я должен преобразовать в другой тип. Нет Span‹что-то›. просто byte[]... public void SetArr(byte[] b) { } public void Run() { var arr = new byte[10]; Span‹byte› bytes = arr; SetArr (байты); } - person Eitan; 21.01.2019
comment
@Eitan, вы не можете напрямую преобразовать тип массива. Выхода нет. Полностью основывайте свой код на span. Таким образом, вы можете преобразовать на месте как массив. - person usr; 21.01.2019
comment
Как мой пример MyConverter, я могу, но это работает только для коротких [] в ushort [] т.е. (short и ushort имеют одинаковый размер. Когда я конвертирую в byte[] он имеет половинный размер, который должен быть размером * 2). Мне нужно использовать управляемый тип, потому что я использую некоторые сторонние библиотеки, параметры которых представляют собой массив (ушортный, короткий или байтовый - мне нужно поддерживать все типы). Кажется глупым, что я не могу просто ссылаться на одну и ту же память (например, на старый «объединение» в C++). - person Eitan; 21.01.2019
comment
@Eitan Хорошо, если сторонняя библиотека не может принять другой тип, вам не повезло. Массив — это заголовок объекта плюс длина плюс данные. Вы не можете создать массив, указывающий на другую память. Это не произвольное правило, но реализовать такое невозможно. Спан для этого. - person usr; 21.01.2019
comment
Найденные вами небезопасные приемы не изменят свойство Length. Если библиотека не использует это свойство, вам может сойти с рук принудительное изменение статического типа ссылки на объект. Вы можете сделать это с помощью класса Unsafe в .NET Core. Это вполне может нарушить работу среды выполнения (особенно GC). Так что это очень рискованная стратегия. - person usr; 21.01.2019
comment
Если вместо short[] s = MyConverter.GetInstance‹short[]›(px); Я делаю: byte[] b = MyConverter.GetInstance‹byte[]›(px); sizeof(b) имеет тот же размер, что и исходный массив, но должен быть удвоен. b и s указывают на одну и ту же память. Я надеюсь, что GC не запутается и освободит только один из них (они используют один и тот же указатель). Фактический размер всегда указывается в байтах. Должен быть способ справиться с этим. Это выглядит слишком глупо, если нет (зачем мне выделять еще 1G байт или любой дополнительный байт только для перемещения данных между функциями с использованием параметров). Я полагаю, что в коде C++ это было намного проще. - person Eitan; 21.01.2019
comment
Кроме того, когда я оцениваю, тип byte[] равен UShort16 и может иметь значения › 256 (так что на самом деле он имеет исходные значения). Может быть, небезопасно - не правильное решение. То, что я ищу, эквивалентно «союзу» в С#. - person Eitan; 21.01.2019
comment
До Span C# не признавал возможности просмотра произвольной памяти. Это сильная сторона C, которой действительно очень не хватало. Современные API часто используют Span, а не массивы. К сожалению, мы теперь застряли с устаревшим способом сделать это. - person usr; 21.01.2019
comment
Обратите внимание, что массивы .NET связывают тип, длину и содержимое. Указатели C имеют только содержимое (даже не длину). Это делает их более гибкими. - person usr; 21.01.2019
comment
В .NET есть объединения (структура с LayoutKind.Explicit). Но они будут для каждого элемента массива. Невозможно наложить два массива. - person usr; 21.01.2019
comment
Span‹byte› подходит, но если arraySpan = MemoryMarshal.Cast‹short, byte›(массив); преобразовать короткий массив в массив байтов. Как я могу преобразовать массив Span‹byte› в byte[]? (Без создания нового блока памяти). - person Eitan; 22.01.2019
comment
Как я могу получить IntPtr диапазона? - person Eitan; 22.01.2019
comment
Это зависит от того, что вы хотите сделать. Возможно, вам потребуется закрепить базовую память. - person usr; 22.01.2019
comment
Итак, как я могу закрепить базовую память? - person Eitan; 22.01.2019
comment
Я предлагаю вам ввести массив контактов .net в поисковую систему. Это лучше объяснено там по сравнению с тем, что я могу поместить в комментарий. Вы не столкнетесь с какими-либо проблемами с закреплением и указателями. Они настолько гибкие, как и следовало ожидать. - person usr; 22.01.2019
comment
Я видел: stackoverflow .com/questions/23254759/, но это может подойти для byte[]. Я не могу сделать что-то вроде: GCHandle.Alloc(myspan, GCHandleType.Pinned) - person Eitan; 22.01.2019
comment
Span нельзя закреплять, потому что он может работать с памятью, которая не закрепляется (например, неуправляемая). Вам нужно отслеживать базовый массив вместе с диапазоном. Закрепите базовый массив. Затем вы можете использовать как указатель, так и диапазон. - person usr; 22.01.2019