【发布时间】:2010-12-15 19:00:55
【问题描述】:
注意:随着我对该问题的了解越来越多,这个问题发生了一些变化,因此请完整阅读。我决定保留它的原始形式,因为它更好地描述了问题是如何被发现和最终解决的。
回到我们项目历史的最黑暗深处,当我们没有像我们可能的那样真正了解 C# 或 CLR 时,我们创建了一个类型,我们称之为 MyType。我们将此类型创建为class,一个引用类型。
但是,很明显MyType 应该是 struct,这是一种值类型,因此我们进行了一些更改以使其如此,一切都很好,直到有一天,我们尝试反序列化一些包含一组数据的数据MyType 值。嗯,不是真的,因为当它是一个引用类型时,那个集合就是一个引用的集合。现在当它反序列化时,集合可以很好地反序列化,使用默认构造函数MyType,然后当实际引用反序列化时,它们是孤立的,给我们留下一个空值集合。
所以,我们想,“让我们在加载时将类型映射到引用类型MyTypeRef,以便正确解析引用,然后转换回我们的真实类型,以便在执行时和重新序列化期间使用”。所以我们这样做了(使用我们自己的活页夹),但是可惜,它不起作用,因为现在我们得到一个错误,告诉我们 MyTypeRef[] 无法转换为 MyType[] 即使我们在 MyTypeRef 之间进行了隐式转换和MyType。
所以,我们陷入了困境。我们如何将序列化为引用类型MyType 的集合反序列化为值类型MyType 的集合?
更新
一些调查(参见下面的 cmets 和代码)表明,新的MyType 的不可变性质以及它用于序列化的ISerializable 导致了真正的问题。我仍然不明白为什么会这样,但是如果我使用private 设置访问器而不是ISerializable,新的MyType 将加载旧的(请注意,如果我使用ISerializable 接口将被调用它,但集合甚至只包含默认值)。
一些代码
// Use a List<T> in a class that also implements ISerializable and
// save an instance of that class with a BinaryFormatter (code omitted)
// Save with this one.
[Serializable]
public class MyType
{
private string test;
public string Test
{
get { return this.test; }
set { this.test = value; }
}
public MyType()
{
}
}
// Load with this one.
[Serializable]
public class MyType : ISerializable
{
private string test;
public string Test
{
get { return this.test; }
set { this.test = value; }
}
public MyType()
{
}
public MyType(SerializationInfo info, StreamingContext context)
{
info.AddValue("test", this.test);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
this.test = info.GetString("test");
}
}
注意,加载时,元素都是空的。从第二个定义中删除 ISerializable 并加载,一切正常。将加载代码上的 class 更改为 struct 并且可以看到相同的行为。好像只有使用 set 访问器才能成功反序列化集合。
更新两个
因此,我发现了问题(请参阅下面的答案),但我怀疑有人会通过阅读我的问题知道答案。我错过了一个重要的细节,当时我什至没有意识到这很重要。我向那些试图提供帮助的人致以最诚挚的歉意。
加载的包含MyType 的集合在GetObjectData 期间立即复制到另一个集合。下面的答案解释了为什么这很重要。这是上面给出的一些附加示例代码,应该提供一个完整的示例:
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
this.myTypeCollection = new List<MyType>();
var loadedCollection = (List<MyType>)info.GetValue(
"myTypeCollection",
typeof(List<MyType>));
this.myTypeCollection.AddRange(loadedCollection);
}
【问题讨论】:
-
我想我们也可以使用活页夹将集合类型映射为我们的临时类型,最初并没有想到这一点,但后来我们不得不为所有可以想象的事情做这件事可能已与该类型一起使用的泛型,我可以看到那里出现的问题。
-
我认为这是使用 BianryFormattet 的?嗯.. 不是一个真正的答案,但我可能会使用旧版本来阅读它,通过 XmlSerializer 将其序列化为(例如)XML,然后切换到新代码以反序列化它。然后不再使用 BF ;) 如果这是一次性操作,这完全有效。
-
我刚刚发现重大变化不是引用类型到值类型,而是我使我的类型不可变。显然,使用
ISerializable而不是属性设置器来加载MyType意味着它在作为集合的一部分序列化时会以不同的方式加载,从而为集合留下一个空项(如果它是引用类型,则值类型为空项)。跨度> -
此外,添加私有设置器并删除
ISerializable意味着一切正常。 -
实际上,BF 根本不使用 setter - 它应该在没有它们的情况下工作。
标签: c# .net-3.5 serialization binaryformatter