Hen*_*rik 5 c# serialization binaryformatter deserialization
我目前正在编写一个双向地图类,我在类的序列化/反序列化方面遇到了一些麻烦(问题在底部).
这是相关课程的部分内容.
/// <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);
}
}
Run Code Online (Sandbox Code Playgroud)
由于forwardMap和inverseMap字典包含完全相同的数据,我的想法是仅序列化其中一个(forwardMap),然后根据反序列化的数据构建另一个(inverseMap).但是,反向映射不会在反序列化构造函数中填充任何数据.看来,在已经执行了类的反序列化构造函数之后,forwardMap字典才被完全反序列化.
有关如何解决此问题的任何想法?
我假设你正在使用BinaryFormatter.
BinaryFormatter是一个图形序列化器.它们不是存储在纯树中的对象,而是分配临时对象ID并在遇到它们时进行存储.因此,当反序列化对象时,不能保证所有引用的对象先前已被反序列化.因此,您的条目可能forwardMap尚未填写.
正常的解决方法是添加IDeserializationCallback逻辑类,并建立您inverseMap和inverseInstance后一切都已经在反序列化OnDeserialization方法.但是,Dictionary<TKey, TValue>也是实现IDeserializationCallback,它引入了一个额外的排序问题:它不能保证在你之前被调用.在这个主题上,微软写道:
从内到外重建对象,并且在反序列化期间调用方法可能具有不期望的副作用,因为调用的方法可能引用在进行调用时尚未反序列化的对象引用.如果被反序列化的类实现了IDeserializationCallback,则在对整个对象图进行反序列化时,将自动调用OnSerialization方法.此时,引用的所有子对象都已完全恢复.哈希表是不使用上述事件侦听器而难以反序列化的类的典型示例.在反序列化期间很容易检索键/值对,但是将这些对象添加回哈希表会导致问题,因为无法保证从哈希表派生的类已被反序列化.因此,不建议在此阶段调用哈希表上的方法.
因此,您可以做几件事:
而不是存储Dictionary<TKey,TValue>,存储数组KeyValuePair<TKey,TValue>.这样做的好处是可以使二进制数据更简单,但需要在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.
Run Code Online (Sandbox Code Playgroud)
即在回调中,OnDeserialization在使用之前调用嵌套字典的方法:
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);
}
Run Code Online (Sandbox Code Playgroud)
([OnDeserialied]如果您愿意,可以在方法中完成.)
顺便说一句,这篇博文称,从包含类的反序列化构造函数调用a的OnDeserialization方法是安全的,而不是稍后调用,所以你可以尝试一下.HashTableOnDeserialization