I have a general understanding on how ISerializable works e.g. how formatter constructs a SerializationInfo
and pass it to the object (let's say Dictionary) and when the foramtter does deserialization, it retrieves SerializationInfo
and calls Dictionary's specail protected constructor and pass this object etc. Below is some code(simplified by me) in regards to Dictionary:
[Serializable]
public class Dictionary<TKey,TValue>: ...ISerializable, IDeserializationCallback {
private SerializationInfo m_siInfo; // Only used for deserialization
// Special constructor to control deserialization
protected Dictionary(SerializationInfo info, StreamingContext context) {
// During deserialization, save the SerializationInfo for OnDeserialization
m_siInfo = info;
}
// implements ISerializable for serialization purpose
public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
info.AddValue("Version", m_version);
info.AddValue("Comparer", m_comparer, typeof(IEqualityComparer<TKey>));
info.AddValue("HashSize", (m_ buckets == null) ? 0 : m_buckets.Length);
if (m_buckets != null) {
KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[Count];
CopyTo(array, 0);
info.AddValue("KeyValuePairs", array, typeof(KeyValuePair<TKey, TValue>[]));
}
}
// implement IDeserializationCallback, so this method will be called after deserialization, which set the dictionary internal state bakc to its original state before serialization
public virtual void OnDeserialization(Object sender) {
if (m_siInfo == null) return; // Never set, return
Int32 num = m_siInfo.GetInt32("Version");
Int32 num2 = m_siInfo.GetInt32("HashSize");
m_comparer = (IEqualityComparer<TKey>)
m_siInfo.GetValue("Comparer", typeof(IEqualityComparer<TKey>));
...// reconstruct the Dictionary's internal states such as buckets, entries etc
}
...
}
I have a question: Why does Dictionaryneeds need to implement IDeserializationCallback rather than just do every thing in the specail constructor?
as canton7 said:
The dictionary's contents are serialized when the dictionary is serialized -- every key and value stored in the dictionary is serialized. The dictionary cannot calculate the hash codes for any of its contents until they've finished being deserialized. Therefore the calculation of the hash codes is deferred until OnDeserialization, by which point each of the keys and values in the dictionary have finished being deserialized, and it is safe to call methods on them
But below is the quote from the book CLR via C#:
When a formatter serializes an object graph, it looks at each object. If its type implements the ISerializable interface, then the formatter ignores all custom attributes and instead constructs a new System.Runtime.Serialization.SerializationInfo object. This object contains the actual set of values that should be serialized for the object.
We can see that it is the SerializationInfo
object will be serialized, the Dictionary object itself won't be serilailized.
As the formatter extracts SerializationInfo
object from the stream, it creates a new Dictionary object (by calling the FormatterServices.GetUninitializedObject
method). Initially, all of this Dictionary object’s fields are set to 0 or null.
And we already have SerializationInfo
object back and passed it to the special constructor, you have all original keys/values before serialization, then you can reconstruct the Dictionary's internal states in the specail constructor just as
protected Dictionary(SerializationInfo info, StreamingContext context) {
if (info == null) return; // Never set, return
Int32 num = info.GetInt32("Version");
Int32 num2 = info.GetInt32("HashSize");
m_comparer = (IEqualityComparer<TKey>)
info.GetValue("Comparer", typeof(IEqualityComparer<TKey>));
...// reconstruct the Dictionary's internal states such as buckets, entries etc
}
Is my understanding correct?
See Question&Answers more detail:os