3

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

情况如下:

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

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

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

为了反序列化,我使用 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<>,是否有一种特定的方式来分隔对象?

4

4 回答 4

4

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

改为以字节大小为每个块添加前缀,并以块为单位读取。

于 2012-04-13T21:22:34.813 回答
4

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

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

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

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

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

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

于 2012-04-13T21:32:55.957 回答
2

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

我将从评论中提取一些信息,因为它令人惊讶但很重要:

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

如果您指定 SequentialLayout,您可以将托管类型位位元组化为字节。http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.aspx这个工具并不为人所知,但它存在,被指定和支持。引用:“类布局属性(AutoLayout、SequentialLayout 和 ExplicitLayout)定义了类实例的字段在内存中的布局方式。”

查看 System.Reflection.TypeAttributes 枚举。它还定义了其他 CLR 级别的属性。C# 不授予对它们的访问权限,但 ilasm.exe 可以。

于 2012-04-13T21:23:37.290 回答
1

我可以使用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);
于 2018-06-27T16:14:00.607 回答