SF Developer SF Developer - 3 months ago 46
C# Question

Fixed Object to Byte Array

I have a simple object that looks like this:

public class Foo
{
public UInt32 One { get; set; }
public UInt32 Two { get; set; }
public UInt32 Three { get; set; }
public UInt32 Four { get; set; }
}


I tried this code I found somewhere on the net:

public byte[] ObjectToByteArray(Object obj)
{
MemoryStream fs = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(fs, obj);
byte[] rval = fs.ToArray();
fs.Close();
return rval;
}


But somehow the returning byte array has a size of 248 bytes.

I would expect it to be 4 bytes x 4 fields = 16 bytes.

QUESTION:

What's the cleanest way to convert a fixed object into a byte array?

And should the resulting array be 16 bytes in size in this case?

Answer

BinaryFormatter saves a lot of type information to be able to deserialize properly. If you want compact serialization or to comunicate via some strict protocol, you will have to do it explictly like this:

public byte[] ToByteArray()
{
    List<byte> result = new List<byte>();

    result.AddRange(BitConverter.GetBytes(One));
    result.AddRange(BitConverter.GetBytes(Two));
    result.AddRange(BitConverter.GetBytes(Three));
    result.AddRange(BitConverter.GetBytes(Four));

    return result.ToArray();
}

here I convert each of your UInt32 into byte array and store it in resulting array.

Edit
Turns out there is another way by using struct and Marshal First you make struct and mark it with attributes like that:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct MyStruct
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string StringField;

    public int IntField;
}

Here LayoutKind.Sequential tells clr to keep fields in memory in same order as declaration. Without Pack = 1 structures can take more memory than required. Like struct with one short field and one byte needs only 3 bytes, but by default it's size will most likely be 4 (processor has instructions for manipulating single byte, 2 bytes and 4 bytes, clr sacrifices one byte per instance of struct to reduce amount of instructions of machine code by half). Now you can use Marshal to copy bytes:

public static byte[] GetBytes<T>(T str)
{
    int size = Marshal.SizeOf(str);
    var bytes = new byte[size];
    IntPtr ptr = Marshal.AllocHGlobal(size);

    try 
    {
         Marshal.StructureToPtr(str, ptr, true);
         Marshal.Copy(ptr, bytes, 0, size);
         return bytes;
    }
    finally 
    {
         Marshal.FreeHGlobal(ptr);
    }
}

Everything works fine with simple types. For complex types such as string you'll have to use MarshalAs attribute and it's using is a bit more complicated (In example I told clr to marshal string as array of fixed 50 bytes size).