我正在编写一个优化的二进制读取器/写入器,用于自己学习。一切工作正常,直到我编写了decimal
的编码和解码测试。我的测试还包括.NET Framework的BinaryWriter
是否为我的BinaryWriter生成兼容的输出,反之亦然。
我主要使用不安全和指针将变量写入字节数组。这些是通过指针和BinaryWriter
编写小数时的结果:
BinaryWriter....: E9 A8 94 23 9B CA 4E 44 63 C5 44 39 00 00 1A 00
unsafe *decimal=: 00 00 1A 00 63 C5 44 39 E9 A8 94 23 9B CA 4E 44
我写小数的代码如下:
unsafe
{
byte[] data = new byte[16];
fixed (byte* pData = data)
*(decimal*)pData = 177.237846528973465289734658334m;
}
使用.NET Framework的BinaryWriter看起来像这样:
using (MemoryStream ms = new MemoryStream())
{
using (BinaryWriter writer = new BinaryWriter(ms))
writer.Write(177.237846528973465289734658334m);
ms.ToArray();
}
Microsoft使它们的BinaryWriter
与decimal
的存储方式不兼容。通过查看参考源,我们可以看到Microsoft使用了一种称为GetBytes的内部方法,这意味着GetBytes的输出与decimals
在内存中的存储方式不兼容。
Microsoft是否有理由以这种方式实现编写decimal
的目的?用unsafe
来实现自己的二进制格式或协议是否会很危险,因为将来小数的内部布局可能会更改?
使用unsafe
的方式比使用GetBytes
调用的BinaryWriter
的效果要好。
答案 0 :(得分:2)
Microsoft本身试图保持decimal
及其组件的对齐尽可能稳定。您还可以在提到的.NET框架参考源中看到这一点:
// NOTE: Do not change the order in which these fields are declared. The
// native methods in this class rely on this particular order.
private int flags;
private int hi;
private int lo;
private int mid;
连同[StructLayout(LayoutKind.Sequential)]
的使用,结构在内存中完全按照这种方式对齐。
由于GetBytes
方法使用的变量是错误的结果,这些变量在内部按自身在结构中对齐的顺序在内部{em> not 中构建decimal
的数据:
internal static void GetBytes(Decimal d, byte[] buffer)
{
Contract.Requires((buffer != null && buffer.Length >= 16), "[GetBytes]buffer != null && buffer.Length >= 16");
buffer[0] = (byte)d.lo;
buffer[1] = (byte)(d.lo >> 8);
buffer[2] = (byte)(d.lo >> 16);
buffer[3] = (byte)(d.lo >> 24);
buffer[4] = (byte)d.mid;
buffer[5] = (byte)(d.mid >> 8);
buffer[6] = (byte)(d.mid >> 16);
buffer[7] = (byte)(d.mid >> 24);
buffer[8] = (byte)d.hi;
buffer[9] = (byte)(d.hi >> 8);
buffer[10] = (byte)(d.hi >> 16);
buffer[11] = (byte)(d.hi >> 24);
buffer[12] = (byte)d.flags;
buffer[13] = (byte)(d.flags >> 8);
buffer[14] = (byte)(d.flags >> 16);
buffer[15] = (byte)(d.flags >> 24);
}
在我看来,相应的.NET开发人员试图将GetBytes呈现的格式调整为little endian,但犯了一个错误。他不仅对decimal
的组件字节进行了排序,还对组件本身进行了排序。 (标志,hi,lo,mid变为lo,mid,hi,flags。)但是,小字节序布局仅适用于不适合整个struct
的字段-尤其是[StructLayout(LayoutKind.Sequential)]
。
我在这里的建议通常是使用Microsoft在其类中提供的方法。因此,与使用GetBytes
相比,我更喜欢任何基于GetBits
或unsafe
的序列化数据的方式,因为Microsoft将以任何方式保持与BinaryWriter
的兼容性。但是,这些评论有点严肃,我不希望Microsoft在这个非常基本的水平上破坏.NET框架。
我很难相信性能非常重要,以unsafe
胜过GetBits
。毕竟,我们在这里谈论decimal
。您仍然可以通过int
将GetBits
的{{1}}推入unsafe
。