8

问题

使用 1.0 版本的程序集序列化的对象(具有私有MethodInfo字段)不会使用该程序集的 1.1 版本反序列化(SerializationException由于未找到所需的方法,将抛出 a)。

有什么改变?

我发现在 .NET 4.5 中,MemberInfovia的序列化机制MemberInfoSerializationHolder已经改变。在过去(直到 .NET 4.0),序列化数据是方法签名(通过简单的MethodInfo.ToString(). 根据 .NET 源代码中的注释,他们添加了通过以下方式获得的第二个签名SerializationToString()

m_signature 存储有时不明确的成员的 ToString() 表示。相同方法或属性的多个重载可以相同 ToString()。m_signature2 存储对每个成员来说应该是唯一的 SerializationToString() 表示。它仅由 4.0 后的 CLR 版本编写和使用。

对于我所看到的MemberInfoSerializationHolder.GetRealObject()使用这个(简化的)代码来解析方法(来自.NET源代码):

for (int i = 0; i < methods.Length; i++)
{ 
    if (m_signature2 != null) // SerializationToString() signature
    {
        if (((RuntimeMethodInfo)methods[i]).SerializationToString().Equals(m_signature2))
        { 
            methodInfo = methods[i];
            break; 
        } 
    }
    else 
    {
        if (methods[i].ToString().Equals(m_signature))
        { 
            methodInfo = methods[i];
            break; 
        }
    }
}

if (methodInfo == null)
    throw new SerializationException(...);

在这种情况下,反序列化失败,因为m_signature2无法找到签名,因为程序集名称包含版本信息,然后String.Equals()将不匹配MyAssembly, Version=1.0.0.0MyAssembly, Version=1.1.0.0引发异常。

问题

如果新搜索失败(至少因为与现有代码的兼容性),我希望框架将失败回到旧的搜索方法。我不明白为什么要与 a 进行此比较,毕竟在运行时解析了所有版本的程序集(并且默认情况下将加载较新的版本),我同意它无法解决那里的程序集版本,但它如果严格搜索失败,可能会删除/忽略它。String.Equals()

我知道序列化 a 很糟糕,MethodInfo但此时此修复可能涉及太多更改(包括架构和代码),并且没有人会在旧代码中开始重构(此外,必须为旧代码和新代码保留存档的二进制兼容性版本,在两个方向)。

到目前为止我还没有尝试过,但是这个问题也适用于代表吗?是否有任何解决方案(带有属性或小的代码更改)来解决这个问题?

4

2 回答 2

2

我能想到的最好方法是实现ISerializableMSDN

在具有属性的对象上实现此接口可以MethodInfo让您完全控制序列化/反序列化。

缺点是您还必须以某种通用方式处理所有其他属性。不过应该是可以的。

于 2013-01-07T17:13:23.123 回答
1

最后,我自己无法完全解决这个问题。我尝试ISerializable使用自定义实现MemberInfoSerializationHolder来模拟旧行为(只是粘贴 4.0 版本的代码)。它适用于存档,但不适用于此场景的旧(已部署)应用程序。我没有找到解决此问题的任何方法,因为已经发布的应用程序将无法工作(除非应用了补丁,但这是不可行的)。

我看到在较新的版本中实现稍微改变了一点,只有当有多个具有相同名称的方法(然后使用签名)时才会预设问题,否则只会选择第一个(也是唯一一个)没有任何额外检查的方法。

于 2013-03-23T21:53:44.420 回答