views:

794

answers:

2

Is there a way in C# to serialize a struct to a binary stream (MemoryStream) such that the binary representation is equivalent to how the struct is visually layed out (i.e. no padding)?

In C/C++, you use #pragma commands to tell the compiler to pack the struct's so that there is no padding between the fields. This is helpful if you have two apps passing messages back and forth via sockets. With packing disabled, you can simply "send" the contents of the struct over the socket and not have to worry about packing each field individually into a binary buffer (also have to do endianness swapping if necessary).

+2  A: 

Not unless you use unsafe code. Use Protocol Buffers or Thrift or something like that; I wouldn't recommend .NET built-in binary serialization based on my experience. You could also serialize/deserialize with BinaryWriter/BinaryReader (use reflection or pre-generate serialization code). As for packing, you can control it with the [StructLayout] attribute.

Anton Tykhyy
For info - protobuf-net doesn't support structs (only classes), and I'm pretty sure dotnet-protobufs doesn't either. I don't know about Thrift.
Marc Gravell
A: 

You can use the [StructLayout] and [FieldOffset] attributes to control the way your struct's fields are packed (google 'marshalling' for more info), and then use the following to generate a binary representation of your struct that can be sent through your network stream:

public static byte[] GetBytes<TStruct>(TStruct data) where TStruct : struct
{
    int structSize = Marshal.SizeOf(typeof(TStruct));
    byte[] buffer = new byte[structSize];
    GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
    Marshal.StructureToPtr(data, handle.AddrOfPinnedObject(), false);
    handle.Free();
    return buffer;
}

The downsides:

  • Its not idiomatic .NET
  • You need unmanaged code permissions
  • The buffer pinning might impact performance
  • Your structure cannot have references (there are special cases for strings and arrays)
Trillian