Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/291.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# *(decimal*)d=XXXm将导致除BinaryWriter.Write(XXXm)之外的另一个输出_C#_Decimal_Unsafe_Binarywriter - Fatal编程技术网

C# *(decimal*)d=XXXm将导致除BinaryWriter.Write(XXXm)之外的另一个输出

C# *(decimal*)d=XXXm将导致除BinaryWriter.Write(XXXm)之外的另一个输出,c#,decimal,unsafe,binarywriter,C#,Decimal,Unsafe,Binarywriter,我正在为自己的学习目的编写一个优化的二进制读写器。一切都很好,直到我为decimals的en-和解码编写了测试。我的测试还包括.NET Framework的BinaryWriter是否生成与我的BinaryWriter兼容的输出,反之亦然 我主要使用不安全和指针将变量写入字节数组。这些是通过指针和BinaryWriter写入十进制数时的结果: BinaryWriter....: E9 A8 94 23 9B CA 4E 44 63 C5 44 39 00 00 1A 00 unsafe *dec

我正在为自己的学习目的编写一个优化的二进制读写器。一切都很好,直到我为
decimal
s的en-和解码编写了测试。我的测试还包括.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;
}
using (MemoryStream ms = new MemoryStream())
{
    using (BinaryWriter writer = new BinaryWriter(ms))
        writer.Write(177.237846528973465289734658334m);

    ms.ToArray();
}
使用.NET Framework的BinaryWriter如下所示:

unsafe
{
    byte[] data = new byte[16];

    fixed (byte* pData = data)
        *(decimal*)pData = 177.237846528973465289734658334m;
}
using (MemoryStream ms = new MemoryStream())
{
    using (BinaryWriter writer = new BinaryWriter(ms))
        writer.Write(177.237846528973465289734658334m);

    ms.ToArray();
}
微软使他们的
BinaryWriter
decimal
存储在内存中的方式不兼容。通过查看referencesource,我们看到Microsoft使用了一个名为GetBytes的内部方法,这意味着GetBytes的输出与
小数
存储在内存中的方式不兼容

微软以这种方式编写
decimal
s有什么原因吗?使用
不安全
的方式来实现自己的二进制格式或协议是否会很危险,因为小数的内部布局将来可能会发生变化


使用
unsafe
方式比使用
BinaryWriter

调用的
GetBytes
性能要好得多,微软自己也试图保持
decimal
及其组件的对齐尽可能稳定。您还可以在所提到的.NET framework的referencesource中看到这一点:

// 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提供的格式调整为little endian,但犯了一个错误。他不仅对
decimal
组件的字节进行排序,还对组件本身进行排序。(标志,hi,lo,mid变为lo,mid,hi,flags。)但小尾端布局仅适用于字段,而不是整个
struct
s-尤其是
[StructLayout(LayoutKind.Sequential)]

我在这里的建议通常是在他们的课堂上使用微软提供的方法。因此,我更喜欢任何基于
GetBytes
GetBits
的方式来序列化数据,而不是使用
unsafe
进行序列化,因为微软将以任何方式保持与
BinaryWriter
的兼容性。然而,这些评论有点严肃,我不希望微软在这个非常基本的层面上打破.NET框架


我很难相信性能对
不安全的
GetBits
更重要。毕竟我们在这里谈论的是十进制。您仍然可以通过
safe
GetBits
int
推送到您的
byte[]

可能重复的@GSerg,公平地说,参考答案只说明它是一种.NET特定格式。它并没有真正回答OP的问题。@Neijwiert好吧,没有人能回答OP的问题,即微软是否愿意改变这种格式。我们只能推测,出于兼容性的原因,这是极不可能的。@GSerg是真的,但我有点希望有人解释当前的实现是如何完成的,因为我不能解释。@Neijwiert看到了<代码>十进制-->十进制.GetBytes(),16字节,应能看到System.decimal类代码
。这是一个输入错误,应该是
GetBits()
decimal
的内部结构记录在。它还解释了为什么
decimal.GetBits()
以特定顺序返回四个
int
s的数组,以及它们的含义。我看不出哪里会有字节顺序不正确的错误。@GSerg在其他评论中已经提到:
GetBits()
与此无关,因为。此外,链接文档没有说明为什么
结构的内存布局与
GetBytes()
返回的布局不同,而我的答案是这样的。你的推理是颠倒的
GetBits()
是起点,因为它是有文档记录的。它返回的东西无法改变。从这一点出发,我们可以看到
internal GetBytes()
返回的数据与文档中的
GetBits()
返回的数据相同,但始终是小端格式(而
GetBits()
自然会使用系统的当前端名),这对于具有不同端名的系统之间的可移植性是有意义的。这两种方法不会改变(这将破坏在假设的改变之前将
十进制
s持久化到存储器中的能力)。相反,构成
十进制
结构的私有字段的内部顺序,在任何时候都可能很容易改变,因为这纯粹是框架内部的问题——但实际上这不会发生,因为没有理由做出这样的改变(例如,引入新字段将破坏
GetBits
GetBytes
;如果必须这样做,它们将产生一种新类型,例如
decimal2
)但即便如此,严格的答案是否定的,也不能保证通过将
十进制*
转换为
字节*
所看到的内容不会改变,而且你不应该依赖它。