我通过网络发送数据。要通过网络发送数据,我将其打包成一个字节数组。
现在数据类型由一个字节和3个浮点组成,总共13个字节。要读这个,我需要知道结构的大小。使用Marshal的sizeOf不会返回序列化字节数组的“正确”大小。
现在我知道发生了什么。 Marshal正在返回我的结构的托管对齐大小。我也知道如果我标记它
[StructLayout(LayoutKind.Sequential,
Pack = 1)]
然后它将返回“正确”的大小。我的问题是什么是最好的方法?我知道它在填充位中添加的原因是因为处理器喜欢所有要对齐的东西(如果我没有弄错,请随意纠正我!)
性能损失是否值得这样做,所以我可以使用sizeof来反序列化数组,或者更好的是使用我自己计算的常量来读取数组,或者采用几个字节的大小惩罚在我的数组中填充它(我最不喜欢的选项。)
如果有什么我误解为什么Marshal想要调整记忆或其他任何东西请随意启发我并给出关于哪种方法最好的建议。
答案 0 :(得分:2)
我通过网络发送数据。要通过网络发送数据,我将其打包成一个字节数组。
不要这样做。
Marshal
和StructLayout
旨在用于本地互操作,而不是用于网络IO和seralization。
为IO序列化对象实例的正确方法是手动编写,这样就可以精确控制序列化表示,并保证格式正确(在多字节值的字节顺序下)。使用BinaryWriter
(或者更好的是,Jon Skeet' MiscUtil. EndianBinaryReader
:http://jonskeet.uk/csharp/miscutil/)。
struct Foo {
public const Int32 Size = sizeof(Byte) + (sizeof(Single) * 3);
public Byte B;
public Single F1;
public Single F2;
public Single F3;
public void Serialize(EndianBinaryWriter wtr) {
wtr.Write( Size );
wtr.Write( B );
wtr.Write( F1 );
wtr.Write( F2 );
wtr.Write( F3 );
}
public static Foo Deserialize(EndianBinaryReader rdr) {
if( rdr.ReadInt32() != Size ) throw new ...
Foo f;
f.B = rdr.ReadByte();
f.F1 = rdr.ReadSingle();
f.F2 = rdr.ReadSingle();
f.F3 = rdr.ReadSingle();
return f;
}
}
将此视为一种ISerializable
- 精简版。您可以在网络程序中使用此对象,方法是使用与您的开放网络套接字连接相对应的Stream
对象,然后将其包裹在EndianBinaryWriter|Reader
对象周围,并通过这些对象执行所有IO操作。作为奖励,您可以免费获得磁盘序列化:只需使用FileStream
而不是网络流。