C# 反序列化时字典为空

C# 反序列化时字典为空,c#,serialization,deserialization,binaryformatter,C#,Serialization,Deserialization,Binaryformatter,我目前正在编写一个双向映射类,在类的序列化/反序列化方面遇到了一些问题(下面的问题) 以下是本课程中相关的部分 /// <summary> /// Represents a dictionary where both keys and values are unique, and the mapping between them is bidirectional. /// </summary> /// <typeparam name="TKey"> The t

我目前正在编写一个双向映射类,在类的序列化/反序列化方面遇到了一些问题(下面的问题)

以下是本课程中相关的部分

/// <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);
        }
 }
//
///表示一个字典,其中键和值都是唯一的,并且它们之间的映射是双向的。
/// 
///字典中键的类型。
///字典中值的类型。
[可序列化]
公共类双向字典:IDictionary、IEquatable、ISerializable、IDeserializationCallback
{
/// 
///将键映射到值的字典。
/// 
私有只读字典转发映射;
/// 
///将值映射到键的字典。
/// 
私有只读字典反向映射;
/// 
///字典的一个实例,其中值是键,键是值。
/// 
专用只读双向对话倒置装置;
/// 
///使用序列化数据初始化dictionary类的新实例。
/// 
///序列化信息。
///瑞典化背景。
受保护的双向对话(SerializationInfo信息、StreamingContext上下文)
{
this.forwardMap=(Dictionary)info.GetValue(“underyingdictionary”,typeof(Dictionary));
this.inverseMap=新字典(
前进地图,计数,
(IEqualityComparer)info.GetValue(“反向比较”,类型为(IEqualityComparer));
//此时forwardMap始终为空。
foreach(forwardMap中的KeyValuePair条目)
逆映射添加(entry.Value,entry.Key);
this.inverseInstance=新的双向字典(this);
}
/// 
///获取序列化字典所需的数据。
/// 
///序列化信息。
///序列化上下文。
public void GetObjectData(SerializationInfo信息、StreamingContext上下文)
{
info.AddValue(“UnderlinedDictionary”,forwardMap);
信息添加值(“反向比较器”,反向映射比较器);
}
}
由于forward和inverseMap字典包含完全相同的数据,我的想法是只序列化其中一个(forwardMap),然后在反序列化时从其数据构建另一个(inverseMap)。但是,inverseMap没有在反序列化构造函数中填充任何数据。forwardMap字典似乎只有在类的反序列化构造函数已经执行之后才被完全反序列化


你知道如何解决这个问题吗?

我假设你正在使用

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

通常的解决方法是向类中添加逻辑,并在方法中的所有内容都反序列化后构建
inverseMap
inverseInstance
。但是,它也实现了,这引入了一个额外的排序问题:不能保证在调用之前就调用了它。关于这个问题:

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

因此,您可以做以下几件事:

  • 与其存储
    字典
    ,不如存储
    键值对
    的数组。这样做的好处是使二进制数据更简单,但需要在
    GetObjectData()
    方法中分配数组

  • 或遵循以下建议:

    也就是说,在回调中,在使用嵌套字典之前,先调用它的
    方法

    public partial class BidirectionalDictionary<TKey, TValue> : IDeserializationCallback
    {
        public void OnDeserialization(object sender)
        {
            this.forwardMap.OnDeserialization(sender);
            foreach (KeyValuePair<TKey, TValue> 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<TValue, TKey>(this);
        }
    
    公共部分类双向dictionalDictionary:IDeserializationCallback
    {
    公共序列化(对象发送方)
    {
    此.forwardMap.OnDeserialization(发送方);
    foreach(forwardMap中的KeyValuePair条目)
    {
    this.inverseMap.Add(entry.Value,entry.Key);
    }
    //inverseInstance将不再是只读的,因为它是在后反序列化回调中分配的。
    this.inverseInstance=新的双向字典(this);
    }
    
    (如果愿意,您可以用一种方法代替。)


  • 顺便提一下,声明从包含类的反序列化构造函数调用
    哈希表的
    OnDeserialization
    方法是安全的,而不是稍后从
    OnDeserialization
    ,因此您可以尝试一下。

    我假设您正在使用

    BinaryFormatter
    是一个图形序列化程序。对象不是存储在纯树中,而是分配给它们临时对象ID和
    public partial class BidirectionalDictionary<TKey, TValue> : IDeserializationCallback
    {
        public void OnDeserialization(object sender)
        {
            this.forwardMap.OnDeserialization(sender);
            foreach (KeyValuePair<TKey, TValue> 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<TValue, TKey>(this);
        }