Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic BitConverter-like method?

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!

like image 433
Ryan Stecker Avatar asked Sep 21 '09 16:09

Ryan Stecker


3 Answers

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.

like image 53
Reed Copsey Avatar answered Sep 21 '22 18:09

Reed Copsey


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.

like image 28
Pavel Minaev Avatar answered Sep 18 '22 18:09

Pavel Minaev


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.

like image 25
Yuri Astrakhan Avatar answered Sep 20 '22 18:09

Yuri Astrakhan