Общий BitConverter-подобный метод? - PullRequest
5 голосов
/ 21 сентября 2009

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

Я создал следующий класс:


public class DataStream
{
    public int Offset { get; set; }

    public byte[] Data { get; set; }

    public T Read<T>() where T : struct
    {
        unsafe
        {
            int dataLen = Marshal.SizeOf( typeof( T ) );
            IntPtr dataBlock = Marshal.AllocHGlobal( dataLen );


            Marshal.Copy( Data, Offset, dataBlock, dataLen );


            T type = *( ( T* )dataBlock.ToPointer() );

            Marshal.FreeHGlobal( dataBlock );

            Offset += dataLen;

            return type;
        }
    }
}

Теперь, если оставить в стороне проблемы с выделением ресурсов, этот код не компилируется с этим сообщением:

Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')

Что, кажется странным, потому что вы должны быть в состоянии выполнить вышеупомянутые операции, основываясь на ограничении where T : struct для метода.

Если этот код ужасно неправильный, есть ли простой способ взять серию байтов и привести их к типу 'T'?

Спасибо!

Ответы [ 5 ]

9 голосов
/ 21 сентября 2009

Вместо того, чтобы пытаться сделать это с помощью манипулятора указателя, вы должны переключить свой код на использование Mashal.PtrToStructure . Этот метод специально разработан для этого сценария.

7 голосов
/ 21 сентября 2009

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

Что, кажется странным, потому что вы должны иметь возможность выполнять вышеуказанные операции на основе ограничения where T: struct на метод.

Не совсем. Вы можете иметь необработанные указатели на неуправляемых типов . Это определяется следующим образом в спецификации языка C # (18.2):

В отличие от ссылок (значений ссылочных типов) сборщик мусора не отслеживает указатели - сборщик мусора не знает указателей и данных, на которые они указывают. По этой причине указателю не разрешено указывать на ссылку или на структуру, которая содержит ссылки, а ссылочный тип указателя должен быть неуправляемого типа . неуправляемый тип - это любой тип, который не является ссылочным типом и не содержит полей ссылочного типа на любом уровне вложенности. Другими словами, неуправляемого типа является одним из следующих:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal или bool.
  • Любой тип enum .
  • Любой тип указателя .
  • Любой пользовательский struct-type , который содержит поля только неуправляемых типов .

Таким образом, существует довольно много ограничений, и для универсального метода T:struct может соответствовать или не соответствовать им для какого-либо конкретного экземпляра, поэтому конструкция типа T* недопустима. Было бы неплохо иметь специальное ограничение параметров универсального типа для покрытия неуправляемых типов, но в настоящее время в CLR его нет.

2 голосов
/ 25 сентября 2009

В какой-то момент я написал эту статью , объясняющую, как именно это сделать, но во много раз быстрее, чем Marshal.PtrToStructure. Пример кода использует динамическое генерирование кода для копирования универсального типа T в / из потока битов.

1 голос
/ 24 октября 2012

Предполагая, что:

  • Последовательная или явная структура (в противном случае очень плохая идея)
  • Правильный размер данных (вы должны предварительно проверить и выбросить)

unsafe TStruct BytesToStructure<TStruct>(byte[] data) where TStruct : struct
{
    fixed (byte* dataPtr = data)
        return (TStruct)Marshal.PtrToStructure(new IntPtr(dataPtr), typeof(TStruct));
}

unsafe byte[] StructureToBytes<TStruct>(TStruct st) where TStruct : struct
{
    var bytes = new byte[Marshal.SizeOf(st)];
    fixed (byte* ptr = bytes) Marshal.StructureToPtr(st, new IntPtr(ptr), true);
    return bytes;
}
0 голосов
/ 05 июня 2012

Я написал это некоторое время назад, чтобы сделать то же самое: http://www.codeproject.com/Articles/33713/Generic-BinaryReader-and-BinaryWriter-Extensions

Однако вам придется добавить проект C ++ / CLI в решение Visual Studio.

...