首先,关于二进制序列化的一些事实(如果您只对解决方案感兴趣,请跳过它们):
- 二进制序列化的目标是制作对象的“按位”副本。这通常涉及私有字段的序列化,这可能会因版本而异。如果反序列化总是在与序列化相同的过程中发生(典型用例:深度克隆、撤消/重做等),这不是问题。
- 因此,如果反序列化可能发生在不同的环境中(包括不同的平台、框架版本、不同版本的程序集,甚至是同一程序集的混淆版本),则不建议使用二进制序列化。如果您知道其中任何一个都适用于您的情况,那么请考虑使用公共成员的基于文本的序列化,例如 XML 或 JSON 序列化。
- 微软似乎开始放弃
BinaryFormatter
. 虽然它只会在 .NET 5 中被删除/标记为过时(尽管可以用作包),但 .NET Core 2/3 中也有许多类型,它们曾经在 .NET Framework 中可序列化但在 .NET Core 中不再可序列化(例如,、、、、、委托等Type
)。Encoding
MemoryStream
ResourceSet
如果您仍然确定要使用BinaryFormatter
以下选项解决问题:
1.最简单的情况:只有汇编版本发生了变化
assemblyBinding
您可以在 app.config 文件中添加一个简单的。只需将实际版本放入newVersion
属性中即可。
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="MyAssembly" publicKeyToken="null" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
2. 程序集名称和/或类型名称也已更改(或者如果您更喜欢编程解决方案)
IFormatter
实现(因此也BinaryFormatter
)具有Binder
属性。您可以使用它来控制程序集/类型名称解析:
internal class MyBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
// mapping the known old type to the new one
if (assemblyName.StartsWith("MyAssembly, ") && typeName == "MyNamespace.MyOldType")
return typeof(MyNewType);
// for any other type returning null to apply the default resolving logic:
return null;
}
}
用法:
var formatter = new BinaryFormatter { Binder = new MyBinder() };
return (MyNewType)formatter.Deserialize(myStream);
如果您只需要一个对程序集版本不敏感的解析器,您可以使用WeakAssemblySerializationBinder
.
3.新型的内部结构也发生了变化
由于 OP 没有涵盖这种情况,我不会太深入细节。TL;DR:在这种情况下,您需要设置IFormatter.SurrogateSelector
属性。Binder
如果类型名称和内部布局都已更改,则可以将其与属性一起使用。如果您有兴趣,在课堂的备注部分有一些可能的子案例案例。CustomSerializerSurrogateSelector
最后的想法:
- 问题中的错误消息暗示使用
BinaryFormatter
可能不是您目标的最佳选择。仅当您确定要使用二进制序列化时才使用上述解决方案。否则,您可以尝试使用 XML 或 JSON 序列化,它们基本上由公共成员序列化类型,并且不存储任何组装信息。
- 如果你想使用我上面链接的活页夹/代理选择器,你可以从NuGet下载库。它实际上还包含一个替代二进制序列化程序(免责声明:由我编写)。尽管它本身支持许多简单的类型和集合(因此没有程序集标识存储在序列化流中),但您可能会面临与问题中出现的相同的问题。