Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can OnDeserializedAttribute be used instead of IDeserializationCallback interface?

As MSDN states here, it can. But I've spent 2 hours digging mscorlib code, because in some cases the BinaryFormatter called my method marked with OnDeserialized BEFORE deserialization constructor. That is, the order was

OnDeserializing(StreamingContext context)
OnDeserialized(StreamingContext context)
.ctor(SerializationInfo info, StreamingContext context)

While I was expecting it to be

OnDeserializing(StreamingContext context)
.ctor(SerializationInfo info, StreamingContext context)
OnDeserialized(StreamingContext context)

And the final point. When I implemented IDeserializationCallback interface, its method OnDeserialization was called AFTER constructor, as I wanted and expected.

I tried to reproduce this on some simple class structure, but there everything worked fine. In our project the objects graph being serialized is very complex, so I do not know where to dig. Inspecting the mscorlib code with reflector did not help a lot - the deserialization code is too complicated for me to figure out where the problem comes from.

So, does anybody know what could be causing such problem? We use the assumption that OnDeserialized is called BEFORE the constructor in several other places so I am scared now that it is not very reliable...

Thanks!

like image 883
ironic Avatar asked Oct 26 '10 12:10

ironic


1 Answers

Finally, I have the answer to my own question, if anyone would be interested. Consider the example in the end of this post. There are two classes, instances of which contain references to each other. Under such conditions there is no possibility that deserializing constructors of both instances are passed with constructed objects. So serializer first calls one of constructors passing it an unconstructed instance of second type and then calls constructor of that object, passing it constructed instance of first type. In such a way it helps us to restore objects connections, so it is really the best it can do!

Next, OnDeserializing and OnDeserialized callbacks in such cases may be called as I pointed in the question, while OnDeserialization method of IDeserializationCallback is always called after the COMPLETE objects graph has been deserialized, exactly as it is stated in its specification.

Keeping all the above in mind, I find it the best to use IDeserializationCallback interface to do any post-deserialization processing I need. In that case I am sure that constructors are called for all objects and I can do necessary modifications in a 'safe' way.

      [Serializable]
      class One :ISerializable, IDeserializationCallback
      {
           public Two m_two;
           public One() {}
           public One(SerializationInfo info, StreamingContext context)
           {
                var two = (Two)info.GetValue("m_two", typeof(Two));
                m_two = two;
           }
           public void GetObjectData(SerializationInfo info, StreamingContext context)
           {
                info.AddValue("m_two", m_two);
           }
           private bool m_onDeserializing;
           private bool m_onDeserialized;
           private bool m_callback;
           public void OnDeserialization(object sender)
           {
                m_callback = true;
           }
           [OnDeserializing]
           void OnDeserializing(StreamingContext context)
           {
                m_onDeserializing = true;
           }

           [OnDeserialized]
           void OnDeserialized(StreamingContext context)
           {
                m_onDeserialized = true;
           }
      }

      [Serializable]
      private class Two : ISerializable, IDeserializationCallback
      {
           public Two(){}
           public One m_one;
           public Two(SerializationInfo info, StreamingContext context)
           {
                var one = (One)info.GetValue("m_one", typeof(One));
                m_one = one;
           }
           public void GetObjectData(SerializationInfo info, StreamingContext context)
           {
                info.AddValue("m_one", m_one);
           }
           private bool m_onDeserializing;
           private bool m_onDeserialized;
           private bool m_callback;
           public void OnDeserialization(object sender)
           {
                m_callback = true;
           }
           [OnDeserializing]
           void OnDeserializing(StreamingContext context)
           {
                m_onDeserializing = true;
           }
           [OnDeserialized]
           void OnDeserialized(StreamingContext context)
           {
                m_onDeserialized = true;
           }
      }

      [STAThread]
      static void Main()
      {
           var one = new One();
           one.m_two = new Two();
           one.m_two.m_one = one;

           BinaryFormatter formatter = new BinaryFormatter();
           MemoryStream mss =new MemoryStream();
           formatter.Serialize(mss, one);
           mss.Position = 0;
           var deserialize = formatter.Deserialize(mss);
      }
like image 198
ironic Avatar answered Sep 21 '22 23:09

ironic