Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.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# 为什么浮点的二进制序列化使用55字节?_C#_Serialization - Fatal编程技术网

C# 为什么浮点的二进制序列化使用55字节?

C# 为什么浮点的二进制序列化使用55字节?,c#,serialization,C#,Serialization,我的任务是获取数百万个浮点数,并将它们以5000个二进制数分批存储在数据库中。这迫使我学习有关序列化性能的有趣内容 令我惊讶的一件事是序列化数据的大小,比我预期的大了十倍。这个测试向我展示了一个四字节浮点被序列化为55字节,一个八字节双精度浮点被序列化为59字节 这里发生了什么?我希望它只是将浮点值拆分为四个字节。其他51个字节是什么 private void SerializeFloat() { Random rnd = new Random(); IFormatter iFo

我的任务是获取数百万个浮点数,并将它们以5000个二进制数分批存储在数据库中。这迫使我学习有关序列化性能的有趣内容

令我惊讶的一件事是序列化数据的大小,比我预期的大了十倍。这个测试向我展示了一个四字节浮点被序列化为55字节,一个八字节双精度浮点被序列化为59字节

这里发生了什么?我希望它只是将浮点值拆分为四个字节。其他51个字节是什么

private void SerializeFloat()
{
    Random rnd = new Random();
    IFormatter iFormatter = new BinaryFormatter();

    using (MemoryStream memoryStream = new MemoryStream(10000000))
    {
        memoryStream.Capacity = 0;
        iFormatter.Serialize(memoryStream, (Single)rnd.NextDouble());
        iFormatter.Serialize(memoryStream, rnd.NextDouble());
    }
}

序列化不仅仅是将位和字节临时发送到流。序列化是结构化输出。这种结构解释了您的实际差异。框架编码额外的信息,让它知道序列化数据中对象的类型和数量,以及许多其他可能性。这是一个实现细节,最好不要管它


如果您需要非结构化输出,.NET序列化会抛出一组信息,而不是双字节的实际8字节(类型信息等)。您可以使用文件
,然后写入通过
字节[]位转换器.GetBytes(double)
二进制编写器
类获取的字节

有许多替代.NET序列化的方法:

  • 文本格式
    • XML
    • JSON
  • 二进制格式
    • 谷歌协议缓冲区
    • 信息包
这些都有其利弊。我特别喜欢并鼓励你看一看。例如,它将使用9个字节来存储一个自描述的
double

二进制序列化是类型安全的。它确保当您反序列化数据时,您将得到完全相同的对象

为了实现这一点,BinaryFormatter添加了有关序列化对象类型的附加数据。你看到了额外的开销。您可以通过序列化到文件流并使用十六进制查看器查看生成的文件来查看它。您将看到后面的字符串,如“System.Single”,类型名称和“m_value”,存储值的字段名称。减少开销的一个好方法是,比如说,序列化一个数组


BinaryWriter正好相反,非常紧凑,但不安全。在这两者之间有很多选择。

因为我决定写这篇文章的人可能会感兴趣,序列化的.NET对象的二进制格式是什么样子,我们如何正确解释它?

我所有的研究都是基于规范的



示例类:

为了有一个工作示例,我创建了一个名为
a
的简单类,它包含两个属性,一个字符串和一个整数值,它们分别称为
SomeString
SomeValue

A
如下所示:

[Serializable()]
public class A
{
    public string SomeString
    {
        get;
        set;
    }

    public int SomeValue
    {
        get;
        set;
    }
}
对于序列化,我使用了
二进制格式化程序
,当然:

BinaryFormatter bf = new BinaryFormatter();
StreamWriter sw = new StreamWriter("test.txt");
bf.Serialize(sw.BaseStream, new A() { SomeString = "abc", SomeValue = 123 });
sw.Close();
可以看出,我传递了类
a
的一个新实例,其中包含
abc
123
作为值



示例结果数据:

如果我们在十六进制编辑器中查看序列化结果,会得到如下结果:

[Serializable()]
public class A
{
    public string SomeString
    {
        get;
        set;
    }

    public int SomeValue
    {
        get;
        set;
    }
}



让我们解释示例结果数据:

根据上述规范(这里是PDF的直接链接:),流中的每个记录都由
RecordTypeEnumeration
标识。第
2.1.2.1节记录类型计数规定:

此枚举标识记录的类型。每个记录(MemberPrimitiveUnTyped除外)都以记录类型枚举开始。枚举的大小为一个字节



SerializationHeaderRecord:

因此,如果我们回顾我们得到的数据,我们可以开始解释第一个字节:

2.1.2.1 RecordTypeEnumeration
中所述,
0
值标识
SerializationHeaderRecord
中指定的
SerializationHeaderRecord

SerializationHeaderRecord记录必须是二进制序列化中的第一条记录。此记录包含格式的主要版本和次要版本,以及顶部对象和标题的ID

它包括:

  • RecordTypeEnum(1字节)
  • RootId(4字节)
  • HeaderId(4字节)
  • 主版本(4字节)
  • MinorVersion(4字节)


有了这些知识,我们可以解释包含17个字节的记录:

00
表示
RecordTypeEnumeration
,在本例中,它是
SerializationHeaderRecord

01 00
表示
RootId

如果序列化流中不存在BinaryMethodCall或BinaryMethodReturn记录,则此字段的值必须包含序列化流中包含的类、数组或BinaryObjectString记录的ObjectId

因此,在我们的例子中,这应该是值为
1
ObjectId(因为数据是使用little endian序列化的),我们希望能再次看到;-)

FF-FF
表示
标题ID

01 00
表示
主要版本

00
表示
MinorVersion




二元图书馆:

按照规定,每条记录必须以
RecordTypeEnumeration
开头。随着最后一条记录的完成,我们必须假设一条新的记录开始了。

让我们解释下一个字节:

正如我们所看到的,在我们的示例中,
SerializationHeaderRecord
后跟
BinaryLibrary
记录:

二元图书馆记录协会