我有一个简单的对象,如下所示:
public class Foo
{
public UInt32 One { get; set; }
public UInt32 Two { get; set; }
public UInt32 Three { get; set; }
public UInt32 Four { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我尝试了这个代码,我发现网上的某个地方:
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;
}
Run Code Online (Sandbox Code Playgroud)
但不知何故,返回的字节数组的大小为248字节.
我希望它是4字节x 4字段= 16字节.
问题:
将固定对象转换为字节数组的最简洁方法是什么?
在这种情况下,结果数组的大小应该是16个字节吗?
BinaryFormatter保存了许多类型信息,以便能够正确反序列化.如果你想要紧凑的序列化或通过一些严格的协议进行通信,你将必须明确地这样做:
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();
}
Run Code Online (Sandbox Code Playgroud)
在这里,我将每个UInt32转换为字节数组并将其存储在结果数组中.
编辑
结果有另一种方法,使用struct和Marshal
首先你struct用这样的属性制作和标记它:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct MyStruct
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public string StringField;
public int IntField;
}
Run Code Online (Sandbox Code Playgroud)
这里LayoutKind.Sequential告诉clr将内存中的字段保存为与声明相同的顺序.没有Pack = 1结构可以占用比所需更多的内存.struct与一个short字段一样,一个字段byte只需要3个字节,但默认情况下它的大小很可能是4(处理器有操作单字节,2字节和4字节的指令,clr牺牲每个struct实例一个字节来减少指令量机器代码减半).现在您可以Marshal用来复制字节:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
简单类型的一切都很好.对于复杂类型,例如string你必须使用MarshalAs属性,它的使用有点复杂(例如我告诉clr将字符串编组为固定的50字节大小的数组).