内置的基于BinaryFormatter的.Net序列化有哪些不足之处?

时间:2009-03-31 21:09:31

标签: .net serialization

内置的 BinaryFormatter 基于.Net序列化有哪些不足之处? (性能,灵活性,限制)

如果可能的话,请附上一些代码。

示例:

序列化的自定义对象必须使用[Serializable]属性进行修饰或实现ISerializable接口。

不太明显的例子:

匿名类型无法序列化。

10 个答案:

答案 0 :(得分:21)

如果您的意思是BinaryFormatter

  • 基于字段,是非常不容忍的版本;更改私人实施细节,它会中断(甚至只是changing it to an automatically implemented property
  • 与其他平台不交叉兼容
  • 对新领域不友好
  • 是特定于程序集(元数据被烧毁)
  • 是特定于MS / .NET的(可能是特定于.NET的版本)
  • 不是混淆安全的
  • 不是特别快,或小输出
  • 不适用于轻型框架(CF?/ Silverlight)
  • 有一种令人沮丧的习惯,即吸引你没想到的东西(通常通过event s)

我在这个领域花了很多时间,包括编写一个(免费)实现Google的“协议缓冲区”序列化API for .NET; protobuf-net

这是:

答案 1 :(得分:2)

答案 2 :(得分:2)

通过属性处理数据的版本控制。如果您不担心版本控制,那么这不是问题。如果你是,这是一个很大的问题。

属性方案的问题在于它对于许多琐碎的情况(例如添加新属性)非常流畅,但是当您尝试执行诸如使用不同的新枚举值替换两个枚举值之类的操作时会很快崩溃(或长期持久数据附带的任意数量的常见场景)。

我可以详细描述这些问题。最后,如果您需要,编写自己的序列化程序非常简单...

答案 3 :(得分:1)

如果更改了要序列化的对象,则序列化和存储的所有旧数据都将被破坏。如果您存储在数据库甚至XML中,则更容易将旧数据转换为新数据。

答案 4 :(得分:1)

不能保证您可以在不同的框架(Say 1.0,1.1,3.5)或不同的CLR实现(Mono)之间来回序列化对象,同样,XML更适合此目的。

答案 5 :(得分:1)

浮现在脑海中的另一个问题是:

XmlSerializer类与通用运行时格式化程序位于完全不同的位置。虽然它们与使用非常相似,但XmlSerializer并未实现IFormatter接口。您不能拥有允许您在运行时在BinaryFormatter,XmlSerializer或自定义格式化程序之间简单地交换序列化格式化程序的代码,而无需跳过一些额外的环节。

答案 6 :(得分:0)

  

要序列化的类型必须是   装饰着[Serializable]   属性。

如果你的意思是一个类中的变量,那你错了。公共变量/属性是自动序列化的

答案 7 :(得分:0)

稍微不那么明显的是,对象序列化的性能非常差。

Example

在我的机器上序列化和反序列化100,000个对象的时间:

Time Elapsed 3 ms
Full Serialization Cycle: BinaryFormatter Int[100000]

Time Elapsed 1246 ms
Full Serialization Cycle: BinaryFormatter NumberObject[100000]

Time Elapsed 54 ms
Full Serialization Cycle: Manual NumberObject[100000]

在这个简单的示例中,使用单个Int字段序列化对象所需的速度比手动执行速度慢20倍。当然,序列化流中有一些类型信息。但这几乎不能解释20倍的放缓。

答案 8 :(得分:0)

我同意最后的答案。表现非常糟糕。 最近,我的编码团队完成了将模拟从标准C ++转换为C ++ / CLI的过程。在C ++下,我们有一个手写的持久性机制,它运作得相当好。我们决定使用序列化机制,而不是重写旧的持久性机制 旧的模拟具有1/2和1 Gig之间的内存占用,并且大多数对象具有指向其他对象的指针,以及运行时的1000个对象,将在一分钟内保持为大约10到15 Meg的二进制文件。从文件恢复是可比较的 使用相同的数据文件(并行运行),C ++ / CLI的运行性能大约是C ++的两倍,直到我们执行持久性(新版本中的序列化)写入需要3到5分钟,读取在10到20之间。序列化文件的文件大小约为旧文件的5倍, 基本上我们看到读取时间增加了19倍,写入时间增加了5倍。这是不可接受的,我们正在寻找解决这个问题的方法。

在检查二进制文件时,我发现了一些事项:1。类型和汇编数据以明文形式写入所有类型。这在空间方面效率低下。 2.每种类型的每个对象/实例都写出了膨胀的类型/汇编信息。我们在我们的手持久性mechansim做的一件事是写出一个已知的类型表。当我们在写作中发现类型时,我们在此表中查找它的存在。如果它不存在,则创建一个条目,其中包含类型信息和指定的索引。然后我们将类型infor作为整数传递。 (类型,数据,类型,数据)这种“技巧”会大大减少尺寸。这可能需要两次通过数据,但是可以开发“即时”过程,除了将其添加到表中,然后推送到流,如果我们可以保证从流中重新定位的顺序

我希望重新实现一些核心序列化以这种方式优化它,但是,这些类是密封的!我们可能还会找到一种方法来进行简单操作。

答案 9 :(得分:0)

另一种情况会导致BinaryFormatter抛出异常。

[Serializable]
class SerializeMe
{
    public List<Data> _dataList;
    public string _name;
}

[Serializable]
class Data
{
    public int _t;
}

想象一下SerializeMe今天被序列化了。明天我们决定不再需要类数据并将其删除。因此,我们修改SerializeMe类以删除List。现在无法反序列化旧版本的SerializeMe对象。

解决方案是创建自定义BinaryFormatter以正确忽略额外的类,或者使用空定义保持类Data(不需要保留List成员)。