*(decimal*)d=XXXm 产生不同于 BinaryWriter.Write(XXXm) 的另一个输出
*(decimal*)d=XXXm results in another output than BinaryWriter.Write(XXXm)
我正在编写一个优化的二进制文件 reader/writer 以供我自己学习。一切正常,直到我编写了 decimal
s 的编码和解码测试。我的测试还包括 .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
存储在内存中的方式不兼容。通过查看参考源,我们发现微软使用了一个名为 GetBytes 的内部方法,这意味着 GetBytes 的输出与 decimals
存储在内存中的方式不兼容。
Microsoft 以这种方式实现写入 decimal
是否有原因?使用 unsafe
的方式来实现自己的二进制格式或协议可能是危险的,因为将来小数的内部布局可能会改变?
使用 unsafe
方式比使用 BinaryWriter
调用的 GetBytes
执行得更好。
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
方法使用在内部构建 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 提供的格式适配为小端,但犯了一个错误。他不仅对 decimal
的组件的字节进行排序,而且对组件本身进行排序。 (flags, hi, lo, mid 变为 lo, mid, hi, flags。)但是小端布局只适用于不完整的字段 struct
s - 特别是 [StructLayout(LayoutKind.Sequential)]
.
我的建议通常是使用 Microsoft 在 类 中提供的方法。因此,与使用 unsafe
相比,我更喜欢任何基于 GetBytes
或 GetBits
的数据序列化方式,因为微软将以任何方式保持与 BinaryWriter
的兼容性。但是,评论有点严重,我不希望微软在这个非常基础的层面上破坏 .NET 框架。
我很难相信性能如此重要,以至于 unsafe
比 GetBits
更受青睐。毕竟我们在这里谈论的是decimal
s。您仍然可以通过 unsafe
将 GetBits
的 int
推送到您的 byte[]
.
我正在编写一个优化的二进制文件 reader/writer 以供我自己学习。一切正常,直到我编写了 decimal
s 的编码和解码测试。我的测试还包括 .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
存储在内存中的方式不兼容。通过查看参考源,我们发现微软使用了一个名为 GetBytes 的内部方法,这意味着 GetBytes 的输出与 decimals
存储在内存中的方式不兼容。
Microsoft 以这种方式实现写入 decimal
是否有原因?使用 unsafe
的方式来实现自己的二进制格式或协议可能是危险的,因为将来小数的内部布局可能会改变?
使用 unsafe
方式比使用 BinaryWriter
调用的 GetBytes
执行得更好。
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
方法使用在内部构建 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 提供的格式适配为小端,但犯了一个错误。他不仅对 decimal
的组件的字节进行排序,而且对组件本身进行排序。 (flags, hi, lo, mid 变为 lo, mid, hi, flags。)但是小端布局只适用于不完整的字段 struct
s - 特别是 [StructLayout(LayoutKind.Sequential)]
.
我的建议通常是使用 Microsoft 在 类 中提供的方法。因此,与使用 unsafe
相比,我更喜欢任何基于 GetBytes
或 GetBits
的数据序列化方式,因为微软将以任何方式保持与 BinaryWriter
的兼容性。但是,评论有点严重,我不希望微软在这个非常基础的层面上破坏 .NET 框架。
我很难相信性能如此重要,以至于 unsafe
比 GetBits
更受青睐。毕竟我们在这里谈论的是decimal
s。您仍然可以通过 unsafe
将 GetBits
的 int
推送到您的 byte[]
.