字典在反序列化时为空

我目前正在编写一个双向地图类,我在类的序列化/反序列化方面遇到了一些麻烦(底部的问题)。

这是相关课程的部分内容。

///  /// 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方法中反序列化所有内容之后构建您的inverseMapinverseInstance 。 但是, Dictionary也实现了IDeserializationCallback ,它引入了一个额外的排序问题:它不能保证在你的之前被调用。 在这个主题上, 微软写道 :

从内到外重构对象,并且在反序列化期间调用方法可能具有不期望的副作用,因为调用的方法可能引用在进行调用时尚未反序列化的对象引用。 如果被反序列化的类实现了IDeserializationCallback,则在对整个对象图进行反序列化时将自动调用OnSerialization方法。 此时,引用的所有子对象都已完全恢复。 哈希表是不使用上述事件侦听器而难以反序列化的类的典型示例。 在反序列化期间很容易检索键/值对,但是将这些对象添加回哈希表会导致问题,因为无法保证从哈希表派生的类已经反序列化。 因此,不建议在此阶段调用哈希表上的方法。

因此,您可以做几件事:

  1. 而不是存储Dictionary ,而是存储KeyValuePair的数组。 这样做的好处是可以使二进制数据更简单,但需要在GetObjectData()方法中分配数组。

  2. 或者按照字典参考源中的建议:

     // 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 HashTableOnDeserialization方法,而不是稍后从OnDeserialization ,所以你可以尝试一下。