-1

I am using a code snippet posted here: Casting a byte array to a managed structure

public static class Serializer
{
    public static unsafe byte[] Serialize<T>(T value) where T : unmanaged
    {
        byte[] buffer = new byte[sizeof(T)];

        fixed (byte* bufferPtr = buffer)
        {
            Buffer.MemoryCopy(&value, bufferPtr, sizeof(T), sizeof(T));
        }

        return buffer;
    }

    public static unsafe T Deserialize<T>(byte[] buffer) where T : unmanaged
    {
        T result = new T();

        fixed (byte* bufferPtr = buffer)
        {
            Buffer.MemoryCopy(bufferPtr, &result, sizeof(T), sizeof(T));
        }

        return result;
    }
}

However, my struct that I am passing to the Serialize function has a fixed sized string

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TestStruct
{
    public UInt32 uniqueID;
    public byte type;
    public byte control;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]
    public string UserID;
}

byte[] returnData = Serializer.Serialize<TestStrut>(teststruct);

I get the following error - The type 'Program.TestStruct' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Serializer.Serialize(T)'

If I get rid of the string in the struct, the Serialize function works as intended. How do I modify the serializer class code to accommodate strings?

Rithesh
  • 25
  • 1
  • 6
  • 1
    _non-nullable value type, along with all fields at any level of nesting_ -> `string` is a nullable reference type, so it doesn't match this requirement – Emanuel Vintilă Jul 04 '21 at 17:58
  • Unless this is only one off case, and since if you try this `The 'class', 'struct', 'unmanaged', 'notnull', and 'default' constraints cannot be combined or duplicated, and must be specified first in the constraints list.`, I would think you can remove `UserID` from struct and send it separately. Correlation between two calls won't be easy though. One more way to make `TestStruct` nullable is to provide user id as function `getUserID` for example. But either way it is going to make complicated. –  Jul 04 '21 at 18:19
  • Your memcopy is wrong. It should be copying from offset zero not sizeof(T) : Buffer.MemoryCopy(&value, bufferPtr, 0, sizeof(T)); – jdweng Jul 04 '21 at 18:21

1 Answers1

1

I have changed my Serializer class to the following.

public static class Serializer
{
    public static unsafe byte[] Serialize<T>(T value) where T : struct
    {
        byte[] buffer = new byte[Marshal.SizeOf(value)];
        fixed (byte* bufferPtr = buffer)
        {
            Marshal.StructureToPtr(value, (IntPtr)bufferPtr, true);
        }
        return buffer;
    }

    public static unsafe T Deserialize<T>(byte[] buffer) where T : struct
    {
        fixed (byte* bufferPtr = buffer)
        {
            return (T)Marshal.PtrToStructure((IntPtr)bufferPtr, typeof(T));
        }
    }
}

It now works fine with fixed sized strings in the struct.

Rithesh
  • 25
  • 1
  • 6