确定 .NET 类型的序列化大小和非托管内存效率

Sea*_*man 4 .net c# serialization pointers unmanaged

我的问题是是否可以确定引用类型的序列化大小(以字节为单位)。

情况如下:

我使用 BinaryFormatter 类来序列化基本 .NET 类型,例如:

[Serializable]
public class Foo
{
    public string Foo1 { get; set; }
    public string Foo2 { get; set; } 
}
Run Code Online (Sandbox Code Playgroud)

我将每个项目序列化为 byte[],然后将该段添加到现有 byte[] 的末尾,并在每个段的末尾添加回车符以分隔对象。

为了反序列化,我使用 Marshal.ReadByte() 如下:

List<byte> buffer = new List<byte>();

for (int i = 0; i < MapSize; i++)
{
    byte b = Marshal.ReadByte(readPtr , i); 

    if (b != delim)  // read until encounter a carriage return 
        buffer.Add(b);
    else
        break;
}

readPtr = readPtr + buffer.Count + 1; // incrementing the pointer for the next object

return buffer.ToArray(); 
Run Code Online (Sandbox Code Playgroud)

我相信使用 Marshal.Copy() 会更有效,但我需要提前知道序列化字节段的长度。有没有一种方法可以从正在序列化的类型可靠地计算出这个值,或者我可以使用一种整体更有效的方法?

此外,最终使用回车符也是不可靠的。所以我想知道是否有更标准的方法来分隔对象,无论是通过自定义 BinaryFormatter 还是使用其他标准化最佳实践?例如,如果 BinaryFormatter 序列化为通用 List<>,则 BinaryFormatter 是否有一种特定的方式来分隔对象?

Ken*_*nky 5

没有一个非常好的方法来预先确定序列化长度。BinaryFormatter 协议的规范可在此处获取: http://msdn.microsoft.com/en-us/library/cc236844 (v=prot.10).aspx

我将省去您为了您的目的而阅读它的麻烦:

  1. 它被构建为可扩展的格式。这允许您稍后添加字段并仍然保持与早期实现的某些兼容性。就您的目的而言,这意味着序列化表单的长度没有及时固定。
  2. 它极其脆弱。二进制格式实际上对其中的字段名称进行编码。如果您重命名字段,序列化表单的长度将会改变。
  3. 二进制格式实际上包含序列化编码和对象数据之间的多对一关系。同一个对象可能会以多种不同的方式进行编码,输出有多种不同的字节数(我不会解释为什么要这样写)。

如果您想要一种简单的方法来完成任务,只需创建一个包含所有对象的数组并序列化该单个数组即可。这可以解决您的大部分问题。界定不同对象的所有问题均由 BinaryFormatter 处理。你不会有过多的内存复制。最终的输出将更加紧凑,因为 BinaryFormatter 每次调用只需指定一次字段名称。

最后,我可以告诉您,额外的内存副本并不是当前实现效率低下的主要根源。BinaryFormatter 对反射的使用以及它对序列化输出中的字段名称进行编码的事实使您的效率大大降低。

如果效率至关重要,那么我建议编写一些自定义代码,以“纯旧数据”格式对结构的内容进行编码。然后您就可以控制写入的数量和方式。