Generic BitConverter-like method?

青春壹個敷衍的年華 提交于 2019-11-28 04:24:52

问题


I've recently encountered a situation where I need to create a generic method to read a datatype out of a byte array.

I've created the following class:


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;
        }
    }
}

Now, de-allocation issues aside, this code doesn't compile with this message:

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

Which, seems strange because you should be able to do the above operations based on the where T : struct constraint on the method.

If this code is horribly incorrect, is there any simple way to take a series of bytes and cast them into a 'T' type?

Thanks!


回答1:


Instead of trying to do this via pointer manipulation, you should switch your code to use Mashal.PtrToStructure. This method is specifically designed for this scenario.




回答2:


Since answer has already been given, let me just explain why your original code didn't work for you:

Which, seems strange because you should be able to do the above operations based on the where T : struct constraint on the method.

Not really. You can have raw pointers to unmanaged types. This is defined as follows in the C# language spec (18.2):

Unlike references (values of reference types), pointers are not tracked by the garbage collector — the garbage collector has no knowledge of pointers and the data to which they point. For this reason a pointer is not permitted to point to a reference or to a struct that contains references, and the referent type of a pointer must be an unmanaged-type. An unmanaged-type is any type that isn’t a reference-type and doesn’t contain reference-type fields at any level of nesting. In other words, an unmanaged-type is one of the following:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool.
  • Any enum-type.
  • Any pointer-type.
  • Any user-defined struct-type that contains fields of unmanaged-types only.

So there are quite a few restrictions, and for a generic method, T:struct may or may not conform to them for any particular instantiation, so construct like T* is illegal. It would be nice to have a special generic type parameter constraint to cover unmanaged types, but as it stands, there isn't one in the CLR.




回答3:


At one point I wrote this article explaining how to do exactly that, but many times faster than the Marshal.PtrToStructure. The code sample uses dynamic code generation to copy the generic type T to/from bit stream.




回答4:


Assuming:

  • A Sequential or Explicit Structure (otherwise very bad idea)
  • Correct data sizes (you should pre-check & throw)

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;
}



回答5:


I wrote this a while back to do the same thing: http://www.codeproject.com/Articles/33713/Generic-BinaryReader-and-BinaryWriter-Extensions

You'll have to add a C++/CLI project to your Visual Studio solution though.



来源:https://stackoverflow.com/questions/1455581/generic-bitconverter-like-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!