C# 二进制序列化和 AssemblyFormat,使用 FormatterAssemblyStyle.Full。

4
我很好奇如何使用FormatterAssemblyStyle.Full格式的二进制格式化程序中的程序集格式“破坏”反序列化。
文档说明如下:
在完整模式下,反序列化期间使用的程序集必须与序列化期间使用的程序集完全匹配。
我认为,如果我使用程序集的版本1.0.0.0对对象(_person是一个带有值类型字段的简单类)进行序列化,然后尝试使用程序集v1.2.0.0(更新AssemblyInfo.cs)进行反序列化,那么我会收到反序列化异常。但是,它成功地进行了反序列化。
我是否漏掉了什么?
我正在使用以下方式将其序列化到文件中:
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full;

                using (Stream stream = new FileStream(fileName,
                                                     FileMode.Create,
                                                     FileAccess.Write,
                                                     FileShare.None))
                {
                    formatter.Serialize(stream, _person);
                    stream.Close();
                }

然后使用以下方式反序列化:
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full;

                using (Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    _person = (Person)formatter.Deserialize(stream);
                   stream.Close();
                }

我还注意到使用 FormatterAssemblyStyle.Full 和 FormatterAssemblyStyle.Simple 生成的序列化文件都包含完整的版本信息(例如 Version 1.0.0.0 Culture = neutral,PublicKeyToken = null)- 我原以为 Simple 不会添加所有这些信息? (请参见此处中的格式化程序和程序集名称部分)。
更新1:
到目前为止,我看到唯一的区别是,如果我使用 Simple,则不必为序列化类中的新字段放置 OptionalField 属性,它就可以成功反序列化旧版本。如果使用 Full 则会抛出异常,除非我在新字段上放置 OptionalField 属性。如果使用没有强名称的程序集,这是唯一的区别吗?
有关详细信息,请参见此处
提前致谢。
1个回答

9

FormatterAssemblyStyle.Full的文档实际上包含两个内容:

  1. 将使用Assembly.Load方法加载程序集。
  2. 在反序列化期间使用的程序集必须与序列化期间使用的程序集完全匹配。

通过Assembly.Load加载程序集

当加载程序集时,还会检查程序集版本,但仅在程序集具有强名称的情况下才会进行。有关程序集版本控制的文档如下所示:

运行时区分常规程序集和强命名程序集以进行版本控制。仅针对强命名程序集执行版本检查。

要为程序集添加强名称,请按照如何:使用强名称签名程序集中的步骤操作。 此外,即使为没有强名称的程序集指定了完全限定的程序集名称,AssemblyName文档中也说明了以下内容:

在提供显示名称时,约定StrongName = null或PublicKey = null表示需要绑定和匹配简单命名程序集。

因此,即使使用Assembly.Load方法,运行时也将始终加载没有版本检查的常规程序集。

完全反序列化

并不完全是必须要求整个程序集与反序列化过程中使用的程序集匹配。只有正在反序列化的类(以及对象图中的所有其他类)应该匹配。在每个反序列化类中,只需要匹配字段,可以随意添加新方法。版本容忍性序列化对此进行了更详细的介绍。

总之,如果您没有强命名程序集,则唯一的区别在于反序列化的容忍性如何。因此,根据上下文使用具有OptionalFieldAttributeFull反序列化或Simple反序列化。


谢谢提供的信息;但是为什么文档中对于 Full 模式说:“反序列化时使用的程序集必须与序列化时使用的程序集完全匹配。Assembly 类的 Load 方法用于加载程序集。”——我认为 Load 方法将始终检查版本详细信息,因此会在保存文件版本和反序列化文件的程序集版本之间找到不匹配? - Zivka
我已经更新了我的答案,希望这能稍微澄清一下事情。 - Patko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接