字典在反序列化时为空
我目前正在编写一个双向地图类,我在类的序列化/反序列化方面遇到了一些麻烦(底部的问题)。
这是相关课程的部分内容。
/// /// Represents a dictionary where both keys and values are unique, and the mapping between them is bidirectional. /// /// The type of the keys in the dictionary. /// The type of the values in the dictionary. [Serializable] public class BidirectionalDictionary : IDictionary, IEquatable<BidirectionalDictionary>, ISerializable, IDeserializationCallback { /// /// A dictionary that maps the keys to values. /// private readonly Dictionary forwardMap; /// /// A dictionary that maps the values to keys. /// private readonly Dictionary inverseMap; /// /// An instance of the dictionary where the values are the keys, and the keys are the values. /// private readonly BidirectionalDictionary inverseInstance; /// /// Initializes a new instance of the dictionary class with serialized data. /// /// The serialization info. /// The sserialization context. protected BidirectionalDictionary(SerializationInfo info, StreamingContext context) { this.forwardMap = (Dictionary)info.GetValue("UnderlyingDictionary", typeof(Dictionary)); this.inverseMap = new Dictionary( forwardMap.Count, (IEqualityComparer)info.GetValue("InverseComparer", typeof(IEqualityComparer))); // forwardMap is always empty at this point. foreach (KeyValuePair entry in forwardMap) inverseMap.Add(entry.Value, entry.Key); this.inverseInstance = new BidirectionalDictionary(this); } /// /// Gets the data needed to serialize the dictionary. /// /// The serialization info. /// The serialization context. public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("UnderlyingDictionary", forwardMap); info.AddValue("InverseComparer", inverseMap.Comparer); } }
由于forwardMap和inverseMap字典包含完全相同的数据,我的想法是仅序列化其中一个(forwardMap),然后根据反序列化的数据构建另一个(inverseMap)。 但是,inverseMap不会在反序列化构造函数中填充任何数据。 似乎forwardMap字典只在类的反序列化构造函数已经执行后才被完全反序列化。
有关如何解决此问题的任何想法?
我假设您正在使用BinaryFormatter
。
BinaryFormatter
是一个图形序列化器。 它们不是存储在纯树中的对象,而是分配临时对象ID并在遇到它们时进行存储。 因此,当反序列化对象时,不能保证所有引用的对象先前已被反序列化。 因此,您的forwardMap
的条目可能尚未填写。
正常的解决方法是将IDeserializationCallback
逻辑添加到您的类中,并在OnDeserialization方法中反序列化所有内容之后构建您的inverseMap
和inverseInstance
。 但是, Dictionary
也实现了IDeserializationCallback
,它引入了一个额外的排序问题:它不能保证在你的之前被调用。 在这个主题上, 微软写道 :
从内到外重构对象,并且在反序列化期间调用方法可能具有不期望的副作用,因为调用的方法可能引用在进行调用时尚未反序列化的对象引用。 如果被反序列化的类实现了IDeserializationCallback,则在对整个对象图进行反序列化时将自动调用OnSerialization方法。 此时,引用的所有子对象都已完全恢复。 哈希表是不使用上述事件侦听器而难以反序列化的类的典型示例。 在反序列化期间很容易检索键/值对,但是将这些对象添加回哈希表会导致问题,因为无法保证从哈希表派生的类已经反序列化。 因此,不建议在此阶段调用哈希表上的方法。
因此,您可以做几件事:
-
而不是存储
Dictionary
,而是存储KeyValuePair
的数组。 这样做的好处是可以使二进制数据更简单,但需要在GetObjectData()
方法中分配数组。 -
或者按照字典参考源中的建议:
// It might be necessary to call OnDeserialization from a container if the container object also implements // OnDeserialization. However, remoting will call OnDeserialization again. // We can return immediately if this function is called twice. // Note we set remove the serialization info from the table at the end of this method.
即在你的回调中,在使用之前
OnDeserialization
嵌套字典的OnDeserialization
方法:public partial class BidirectionalDictionary
: IDeserializationCallback { public void OnDeserialization(object sender) { this.forwardMap.OnDeserialization(sender); foreach (KeyValuePair entry in forwardMap) { this.inverseMap.Add(entry.Value, entry.Key); } // inverseInstance will no longer be able to be read-only sicne it is being allocated in a post-deserialization callback. this.inverseInstance = new BidirectionalDictionary (this); } (如果您愿意,可以在
[OnDeserialied]
方法中执行此操作。)
顺便说一句, 这篇博客文章声称可以安全地从包含类的反序列化构造函数 OnDeserialization
HashTable
的OnDeserialization
方法,而不是稍后从OnDeserialization
,所以你可以尝试一下。