ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

c# – *(decimal *)d = XXXm导致另一个输出比BinaryWriter.Write(XXXm)

2019-07-01 09:52:29  阅读:268  来源: 互联网

标签:c decimal unsafe binarywriter


我正在为自己的学习目的编写一个优化的二进制读取器/写入器.一切正常,直到我为小数的编码和解码编写测试.我的测试还包括.NET Framework的BinaryWriter是否为我的BinaryWriter生成兼容的输出,反之亦然.

我主要使用unsafe和指针将变量写入字节数组.当通过指针和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与小数存储在内存中的方式不兼容.通过查看referencesource,我们看到Microsoft使用名为GetBytes的内部方法,这意味着GetBytes的输出与小数存储在内存中的方式不兼容.

微软是否有理由以这种方式实现写小数?使用不安全的方式实现自己的二进制格式或协议可能是危险的,因为小数的内部布局将来可能会改变吗?

使用不安全的方式比使用BinaryWriter调用的GetBytes要好得多.

解决方法:

微软本身试图保持小数和它的组件对齐尽可能稳定.您还可以在.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方法使用的是在内部构建小数数据的变量而不是它们在结构本身中对齐的顺序:

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提供的格式改编为小端,但是犯了一个错误.他不仅订购了小数组件的字节,还订购了组件本身. (flags,hi,lo,mid变为lo,mid,hi,flags.)但是小端布局仅适用于不是整个结构的字段 – 尤其是[StructLayout(LayoutKind.Sequential)].

我的建议通常是使用Microsoft在其课程中提供的方法.因此,我希望任何基于GetBytes或GetBits的方式来序列化数据,而不是使用不安全的方式,因为Microsoft将以任何方式保持与BinaryWriter的兼容性.但是,这些评论有点严肃,我不希望微软在这个基础层面打破.NET框架.

我很难相信性能对于支持GetBits的不安全方式非常重要.毕竟我们在这里谈论小数.你仍然可以通过unsafe将GetBits的int推入你的byte [].

标签:c,decimal,unsafe,binarywriter
来源: https://codeday.me/bug/20190701/1345804.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有