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

时间:2012-04-13 21:13:35

标签: c# .net serialization pointers unmanaged

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

接下来的情况:

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

[Serializable]
public class Foo
{
    public string Foo1 { get; set; }
    public string Foo2 { get; set; } 
}

我将每个项目序列化为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(); 

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

此外,最终使用回车将不可靠。所以我想知道是否有更标准的方法来分隔对象,要么通过自定义BinaryFormatter还是使用其他一些标准化的最佳实践?例如,BinaryFormatter是否有一种特定的方式来分隔对象,如果它的序列化说明,通用List&lt;&gt;?

4 个答案:

答案 0 :(得分:4)

使用字节作为二进制序列化数据的分隔符很糟糕 - 13是完全有效的值,可以是序列化数据的一部分,而不仅仅是“分隔符”。

以字节为单位为每个块添加大小,而不是以块为单位进行读取。

答案 1 :(得分:3)

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

为了您的目的,我会为您省去阅读它的麻烦:

  1. 它是一种可扩展的格式。这允许您稍后添加字段,并仍然保持与早期实现的一些兼容性。出于您的目的,这意味着序列化表单的长度不会及时修复。
  2. 它非常脆弱。二进制格式实际上编码了其中字段的名称。如果您重命名一个字段,序列化表单的长度将会改变。
  3. 二进制格式实际上包含序列化编码和对象数据之间的多对一关系。同一个对象可能会以多种不同的方式进行编码,输出有许多不同的字节数(我不会理解为什么用这种方式编写)。
  4. 如果您想要一种简单的方法,只需创建一个包含所有对象的数组并序列化该单个数组。这解决了大部分问题。分隔不同对象的所有问题都由BinaryFormatter处理。您不会有过多的内存复制。最终输出将更紧凑,因为BinaryFormatter只需在每次调用时指定一次字段名称。

    最后,我可以告诉你,额外的内存副本不是当前实现中效率低下的主要原因。从BinaryFormatter使用反射,以及它在序列化输出中对字段名称进行编码这一事实,你得到的效率要低得多。

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

答案 2 :(得分:2)

您可以使用Marshal.SizeOf来获取结构的原生大小。这仅适用于结构体,我建议您设置StructLayout属性。

我会从评论中提取一些信息,因为它很重要但很重要:

CLR具有元数据功能,用于修复结构或类的本机布局。在C#中,这仅适用于结构。但是类也可以这样使用。

如果指定SequentialLayout,则可以将托管类型按位进行字节操作。 http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.aspx此功能并不为人所知,但它存在,被指定和支持。 Quote:“类布局属性(AutoLayout,SequentialLayout和ExplicitLayout)定义类实例的字段如何在内存中布局。”

查看System.Reflection.TypeAttributes枚举。它还定义了其他CLR级属性。 C#不允许访问它们,但是ilasm.exe可以访问它们。

答案 3 :(得分:0)

使用https://bytes.com/topic/c-sharp/answers/238927-object-size-memory

中的这段代码,我发现根本无法序列化的原因
var m = new System.IO.MemoryStream();
var b = new
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
b.Serialize(m, Obj);
var size = Convert.ToDouble(m.Length);