Reputation: 5259
I have problems getting de-/serialization of a Dictionary working when implementing ISerializable in the enclosing class. It seems to be able to automatically de-/serialize if I just apply the SerializableAttribute. I need to check the deserialized Dictionary in the process however, so I need ISerializable to be working.
I set up a little test to be sure it wasn't due to some other problems. The Test class looks like this:
[Serializable]
class Test : ISerializable
{
private Dictionary<string, int> _dict;
public Test()
{
var r = new Random();
_dict = new Dictionary<string, int>()
{
{ "one", r.Next(10) },
{ "two", r.Next(10) },
{ "thr", r.Next(10) },
{ "fou", r.Next(10) },
{ "fiv", r.Next(10) }
};
}
protected Test(SerializationInfo info, StreamingContext context)
{
// Here _dict.Count == 0
// So it found a Dictionary but no content?
_dict = (Dictionary<string, int>)info.GetValue("foo", typeof(Dictionary<string, int>));
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("foo", _dict, typeof(Dictionary<string, int>));
}
public override string ToString()
{
var sb = new StringBuilder();
foreach (var pair in _dict)
sb.Append(pair.Key).Append(" : ").Append(pair.Value).AppendLine();
return sb.ToString();
}
}
And the main to test it:
static void Main(string[] args)
{
var t1 = new Test();
Console.WriteLine(t1);
var formatter = new BinaryFormatter();
using (var stream = new FileStream("test.test", FileMode.Create, FileAccess.Write, FileShare.None))
formatter.Serialize(stream, t1);
Test t2;
using (var stream = new FileStream("test.test", FileMode.Open, FileAccess.Read, FileShare.Read))
t2 = (Test)formatter.Deserialize(stream);
Console.WriteLine(t2);
Console.ReadLine();
}
The output in the console is the same before and after. But as commented in the Test class, the overloaded constructor doesn't read any content in the deserialized Dictionary.
Am I doing something wrong or is this a bug/subtle side effect?
Upvotes: 3
Views: 1554
Reputation: 6882
Dictionary<TKey, TValue>
implements IDeserializationCallback
and defers the completion of its de-serialization until the whole graph of objects is read back. You can see how it was actually implemented on Reference Source :
protected Dictionary(SerializationInfo info, StreamingContext context)
{
//We can't do anything with the keys and values until the entire graph has been deserialized
//and we have a resonable estimate that GetHashCode is not going to fail. For the time being,
//we'll just cache this. The graph is not valid until OnDeserialization has been called.
HashHelpers.SerializationInfoTable.Add(this, info);
}
To force the completion call _dict.OnDeserialization()
in your code:
protected Test(SerializationInfo info, StreamingContext context)
{
// Here _dict.Count == 0
// So it found a Dictionary but no content?
_dict = (Dictionary<string, int>)info.GetValue("foo", typeof(Dictionary<string, int>));
_dict.OnDeserialization(null);
// Content is restored.
Console.WriteLine("_dict.Count={0}", _dict.Count);
}
PS: HashSet<T>
, SortedSet<T>
, LinkedList<T>
and maybe few other container types exhibit the same behavior
Upvotes: 3