我有一个简单的对象,如下所示:
public class Foo
{
public UInt32 One { get; set; }
public UInt32 Two { get; set; }
public UInt32 Three { get; set; }
public UInt32 Four { get; set; }
}
我尝试了这个代码,我在网上的某个地方找到了:
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;
}
但不知何故,返回的字节数组的大小为248字节 我希望它是4字节x 4字段= 16字节。
问题:
什么是将固定对象转换为字节数组最干净的方法?
在这种情况下,结果数组的大小应该是16个字节吗?
答案 0 :(得分:6)
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();
}
这里我将每个UInt32转换为字节数组并将其存储在结果数组中。
修改强>
事实证明,使用struct
和Marshal
还有另一种方法
首先,您使用struct
并使用以下属性标记它:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct MyStruct
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public string StringField;
public int IntField;
}
这里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);
}
}
简单类型的一切都很好。对于string
之类的复杂类型,你必须使用MarshalAs
属性并且它的使用有点复杂(例如我告诉clr将字符串编组为固定的50字节大小的数组)。
答案 1 :(得分:3)
这是另一种方式......哪种方式最好是可能的意见问题。我喜欢Atomosk的答案。将该答案与演员操作员重载相结合,您就拥有了非常优雅的解决方案。
class Foo
{
public UInt32 One { get; set; }
public UInt32 Two { get; set; }
public UInt32 Three { get; set; }
public UInt32 Four { get; set; }
static public implicit operator byte[](Foo f)
{
MemoryStream m = new MemoryStream(16);
BinaryWriter w = new BinaryWriter(m);
w.Write(f.One);
w.Write(f.Two);
w.Write(f.Three);
w.Write(f.Four);
w.Close();
m.Close();
return m.ToArray();
}
static public explicit operator Foo(byte[] b)
{
Foo f = new Foo();
MemoryStream m = new MemoryStream(b);
BinaryReader r = new BinaryReader(m);
f.One = r.ReadUInt32();
f.Two = r.ReadUInt32();
f.Three = r.ReadUInt32();
f.Four = r.ReadUInt32();
r.Close();
m.Close();
return f;
}
}
class Program
{
static void Main(string[] args)
{
Foo f = new Foo() { One = 1, Two = 2, Three = 3, Four = 4 };
byte[] b = (byte[])f;
Console.WriteLine(b.Length);
f = (Foo)b;
Console.WriteLine("{0} {1} {2} {3}", f.One, f.Two, f.Three, f.Four);
Console.ReadKey(true);
}
}
答案 2 :(得分:2)
请记住,当您使用BinaryFormatter序列化对象时,您会包含类型信息等内容。这会导致你看到的开销。
如果你想将一个对象序列化(在这种情况下)只有16个字节,那么你需要用StreamWriter之类的东西手动完成。
如果大小不是问题,那么在这种情况下Binaryformatter没有错误并且不需要太多代码来执行它,但它不是最有效的内存方式。
编辑:以下是您使用StreamWriter
System.IO.MemoryStream stream = new System.IO.MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(myObject.One); // here's where we actually write the data to the stream
writer.Write(myObject.Two);
writer.Write(myObject.Three);
writer.Write(myObject.Four);
writer.Flush(); // make sure all the data in the stream writer ends up in the
// underlying stream
byte[] result = stream.ToArray(); // here's your resulting byte array
stream.Dispose(); // don't forget to dispose of the stream!
答案 3 :(得分:2)
这是一种手动完成的方法,可以保证16字节。
using System;
using System.IO;
using System.Linq;
public class Foo
{
public UInt32 One { get; set; }
public UInt32 Two { get; set; }
public UInt32 Three { get; set; }
public UInt32 Four { get; set; }
public Foo() {}
public Foo(byte[] array)
{
One = BitConverter.ToUInt32(array,0);
Two = BitConverter.ToUInt32(array,4);
Three = BitConverter.ToUInt32(array,8);
Four = BitConverter.ToUInt32(array,12);
}
public byte[] toByteArray()
{
byte[] retVal = new byte[16];
byte[] b = BitConverter.GetBytes(One);
Array.Copy(b, 0, retVal, 0, 4);
b = BitConverter.GetBytes(Two);
Array.Copy(b, 0, retVal, 4, 4);
b = BitConverter.GetBytes(Three);
Array.Copy(b, 0, retVal, 8, 4);
b = BitConverter.GetBytes(Four);
Array.Copy(b, 0, retVal, 12, 4);
return retVal;
}
}
public class P{
public static void Main(string[] args) {
Foo foo = new Foo();
foo.One = 1;
foo.Two = 2;
foo.Three = 3;
foo.Four = 4;
byte[] arr = foo.toByteArray();
Console.WriteLine(arr.Length);
Foo bar = new Foo(arr);
Console.WriteLine(string.Format("{0} {1} {2} {3}", bar.One, bar.Two, bar.Three, bar.Four));
}
}
输出:
16
1 2 3 4