字典是反序列化空 [英] Dictionary is empty on deserialization

查看:192
本文介绍了字典是反序列化空的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在写一个双向地图类,我对类的序列化/反序列化有些麻烦(底部的问题)。

I'm currently writing a bidirectional map class, and I'm having some troubles with the serialization/deserialization of the class (question at bottom).

这里

/// <summary>
/// Represents a dictionary where both keys and values are unique, and the mapping between them is bidirectional.
/// </summary>
/// <typeparam name="TKey"> The type of the keys in the dictionary. </typeparam>
/// <typeparam name="TValue"> The type of the values in the dictionary. </typeparam>
[Serializable]
public class BidirectionalDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IEquatable<BidirectionalDictionary<TKey, TValue>>, ISerializable, IDeserializationCallback
{

        /// <summary>
        /// A dictionary that maps the keys to values.
        /// </summary>
        private readonly Dictionary<TKey, TValue> forwardMap;

        /// <summary>
        /// A dictionary that maps the values to keys.
        /// </summary>
        private readonly Dictionary<TValue, TKey> inverseMap;

        /// <summary>
        /// An instance of the dictionary where the values are the keys, and the keys are the values. 
        /// </summary>
        private readonly BidirectionalDictionary<TValue, TKey> inverseInstance;

        /// <summary>
        /// Initializes a new instance of the dictionary class with serialized data. </summary>
        /// </summary>
        /// <param name="info"> The serialization info. </param>
        /// <param name="context">  The sserialization context. </param>
        protected BidirectionalDictionary(SerializationInfo info, StreamingContext context)
        {
            this.forwardMap = (Dictionary<TKey, TValue>)info.GetValue("UnderlyingDictionary", typeof(Dictionary<TKey, TValue>));
            this.inverseMap = new Dictionary<TValue, TKey>(
                forwardMap.Count,
                (IEqualityComparer<TValue>)info.GetValue("InverseComparer", typeof(IEqualityComparer<TValue>)));

            // forwardMap is always empty at this point.
            foreach (KeyValuePair<TKey, TValue> entry in forwardMap)
                inverseMap.Add(entry.Value, entry.Key);

            this.inverseInstance = new BidirectionalDictionary<TValue, TKey>(this);
        }

        /// <summary>
        /// Gets the data needed to serialize the dictionary.
        /// </summary>
        /// <param name="info"> The serialization info. </param>
        /// <param name="context">  The serialization context. </param>
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("UnderlyingDictionary", forwardMap);
            info.AddValue("InverseComparer", inverseMap.Comparer);
        }
 }

由于forward-和inverseMap字典包含完全相同数据,我的想法是只序列化其中之一(forwardMap),然后构建另一个(inverseMap)从它的反序列化数据。但是,inverseMap不会在反序列化构造函数中填充任何数据。似乎forwardMap字典只是在类的反序列化构造函数已经执行后完全反序列化。

Since the forward- and inverseMap dictionaries contain the exact same data, my idea was to only serialize one of them (forwardMap), and then build the other (inverseMap) from it's data on deserialization. However, the inverseMap isn't getting populated with any data in the deserialization constructor. It seems the forwardMap dictionary is only completely deserialized after the class' deserialization constructor has already executed.

有关如何解决这个问题的任何想法吗?

Any idea on how to fix this?

推荐答案

我假设你使用 BinaryFormatter

I am assuming you are using BinaryFormatter.

BinaryFormatter 是一个图形序列化程序。而不是存储在纯树中的对象,它们被分配临时对象ID并在遇到它们时存储。因此,当对象被反序列化时,不能保证所有引用的对象先前已被反序列化。因此,您的 forwardMap 中的条目可能尚未填写。

BinaryFormatter is a graph serializer. Rather than objects being stored in a pure tree, they are assigned temporary object ids and stored as they are encountered. Thus when an object is deserialized, it is not guaranteed that all referenced objects have been previously deserialized. Thus it's possible the entries in your forwardMap have not been filled in yet.

正常的解决方法是添加 IDeserializationCallback 逻辑,并构建 inverseMap inverseInstance 之后,在 OnDeserialization 方法。但是, 字典< TKey,TValue> code> 还实现了 IDeserializationCallback ,它引入了一个附加的排序问题:它不能保证在你的之前被调用。关于此主题, Microsoft写入

The normal workaround is to add IDeserializationCallback logic to your class, and build your inverseMap and inverseInstance after everything has been deserialized in the OnDeserialization method. But, Dictionary<TKey, TValue> also implements IDeserializationCallback, which introduces an additional sequencing problem: it is not guaranteed to have been called before yours is. On this topic, Microsoft writes:


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

Objects are reconstructed from the inside out, and calling methods during deserialization can have undesirable side effects, since the methods called might refer to object references that have not been deserialized by the time the call is made. If the class being deserialized implements the IDeserializationCallback, the OnSerialization method will automatically be called when the entire object graph has been deserialized. At this point, all the child objects referenced have been fully restored. A hash table is a typical example of a class that is difficult to deserialize without using the event listener described above. It is easy to retrieve the key/value pairs during deserialization, but adding these objects back to the hash table can cause problems since there is no guarantee that classes that derived from the hash table have been deserialized. Calling methods on a hash table at this stage is therefore not advisable.

因此,你可以做一些事情:

Thus there are a couple things you could do:


  1. 不是存储 Dictionary< TKey,TValue> KeyValuePair< TKey,TValue> 。这具有使您的二进制数据更简单,但需要您在 GetObjectData()方法中分配数组的优点。

  1. Rather than storing a Dictionary<TKey,TValue>, store an array of KeyValuePair<TKey,TValue>. This has the advantage of making your binary data simpler but does require you to allocate the array in your GetObjectData() method.

或按照字典参考来源中的建议操作: / p>

Or follow the advice in the dictionary reference source:

        // 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 方法。

I.e. in your callback, call the OnDeserialization method of your nested dictionary before using it.


b $ b

顺便说一下,这个博客post 声称可以从反序列化调用 HashTable OnDeserialization 方法是安全的构造函数,而不是以后从 OnDeserialization ,所以你可以试试。

Incidentally, this blog post claims that it is safe to call the OnDeserialization method of a HashTable from the deserialization constructor of a containing class, rather than later from OnDeserialization, so you might give that a try.

这篇关于字典是反序列化空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆