.NET Deserialisation with OnDeserializing and OnDeserialized

前端 未结 3 658
一向
一向 2020-12-28 17:02

I use a simple class that is serializable. It has a constructor for the deserialization:

protected MyClass(SerializationInfo info, StreamingContext context)
         


        
相关标签:
3条回答
  • 2020-12-28 17:15

    Now both methods get called before the constructor gets called

    No, the order is:

    • OnDeserializingMethod
    • .ctor
    • OnDeserializedMethod

    And how can a (non-static) method be called before any constructor has been executed?

    Because it cheats and lies; it doesn't create the object with the constructor; no - really. It uses FormatterServices.GetUninitializedObject to allocate vanilla empty space. And then if there is a custom deserialization constructor it invokes the constructor over the top of that object. Nasty. Like this, basically:

    var obj = FormatterServices.GetUninitializedObject(typeof(MyClass));
    var ctor = obj.GetType().GetConstructor(
        BindingFlags.Instance | BindingFlags.Public| BindingFlags.NonPublic,
        null,
        new[] { typeof(SerializationInfo), typeof(StreamingContext) },
        null);
    ctor.Invoke(obj, new object[2]);
    

    IMO they probably should have made this a second method on the ISerializable interface, but for whatever reason: they didn't. A shame really: that would have made it more honest, and avoided people needing to remember to implement the custom constructor.

    Example output:

    .ctor: MyClass
    > serializing
    OnSerializingMethod: MyClass
    GetObjectData: MyClass
    OnSerializedMethod: MyClass
    < serializing
    > deserializing
    OnDeserializingMethod: MyClass
    .ctor: MyClass
    OnDeserializedMethod: MyClass
    < deserializing
    

    Example code:

    using System;
    using System.IO;
    using System.Runtime.CompilerServices;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
    [Serializable]
    class MyClass : ISerializable
    {
        public MyClass() { Trace(); }
        protected MyClass(SerializationInfo info, StreamingContext context) { Trace(); }
        public void GetObjectData(SerializationInfo info, StreamingContext context) { Trace(); }
        void Trace([CallerMemberName] string caller = null)
        {
            System.Console.WriteLine("{0}: {1}", caller, GetType().Name);
        }
        [OnDeserializing()]
        internal void OnDeserializingMethod(StreamingContext context) { Trace(); }
    
        [OnDeserialized()]
        internal void OnDeserializedMethod(StreamingContext context) { Trace(); }
    
        [OnSerializing()]
        internal void OnSerializingMethod(StreamingContext context) { Trace(); }
    
        [OnSerialized()]
        internal void OnSerializedMethod(StreamingContext context) { Trace(); }
    
        static void Main()
        {
            using (var ms = new MemoryStream())
            {
                var orig = new MyClass();
                var ser = new BinaryFormatter();
                System.Console.WriteLine("> serializing");
                ser.Serialize(ms, orig);
                System.Console.WriteLine("< serializing");
                ms.Position = 0;
                System.Console.WriteLine("> deserializing");
                ser.Deserialize(ms);
                System.Console.WriteLine("< deserializing");
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-28 17:15

    [OnDeserializing] Indicates a method to be called just before deserialization [OnDeserialized] Indicates a method to be called just after deserialization

    An [OnDeserializing] method acts as a pseudoconstructor for deserialization, and it is useful for initializing fields excluded from serialization:

    [OnDeserializing] and [OnDeserialized] Deserialization bypasses all your normal constructors as well as field initializers. This is of little consequence if every field partakes in serialization, but it can be problematic if some fields are excluded via [NonSerialized].

    I took this text from Albahari book C# 5.0 in nutshell page 713 check it out online a lot of example and description about your problem.

    Thanks

    0 讨论(0)
  • 2020-12-28 17:30

    The order of the calls depend on wether the object is the root of the serialized tree or some member of an object, which is also serialized in the same object graph. I get the following output with the extended example provided by Marc Gravell:

    SerRoot.ctor
    SerMember.ctor
    > serializing
    SerRoot.OnSerializingMethod
    GetObjectData
    SerMember.OnSerializingMethod
    SerMember.GetObjectData
    SerRoot.OnSerializedMethod
    SerMember.OnSerializedMethod
    < serializing
    > deserializing
    SerRoot.OnDeserializingMethod
    SerMember.OnDeserializingMethod
    SerMember.OnDeserializedMethod
    SerMember.ctor(info, context)
    SerRoot.ctor(info, context)
    SerRoot.OnDeserializedMethod
    < deserializing
    

    Notice that in deserialization SerMember.ctor is called after SerMember.OnDeserializedMethod! This is the code:

            static void Main(string[] args)
        {
            using (var ms = new MemoryStream())
            {
                var orig = new SerRoot();
                var ser = new BinaryFormatter();
                System.Console.WriteLine("> serializing");
                ser.Serialize(ms, orig);
                System.Console.WriteLine("< serializing");
                ms.Position = 0;
                System.Console.WriteLine("> deserializing");
                ser.Deserialize(ms);
                System.Console.WriteLine("< deserializing");
            }
        }
    [Serializable]
    class SerRoot : ISerializable
    {
        public SerMember m;
        public SerRoot()
        {
            System.Console.WriteLine("SerRoot.ctor");
            m = new SerMember();
        }
        protected SerRoot(SerializationInfo info, StreamingContext context)
        {
            System.Console.WriteLine("SerRoot.ctor(info, context)");
            m = info.GetValue("m", typeof(SerMember)) as SerMember;
        }
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            System.Console.WriteLine("GetObjectData");
            info.AddValue("m", m);
        }
        [OnDeserializing()]
        internal void OnDeserializingMethod(StreamingContext context) { System.Console.WriteLine("SerRoot.OnDeserializingMethod"); }
    
        [OnDeserialized()]
        internal void OnDeserializedMethod(StreamingContext context) { System.Console.WriteLine("SerRoot.OnDeserializedMethod"); }
    
        [OnSerializing()]
        internal void OnSerializingMethod(StreamingContext context) { System.Console.WriteLine("SerRoot.OnSerializingMethod"); }
    
        [OnSerialized()]
        internal void OnSerializedMethod(StreamingContext context) { System.Console.WriteLine("SerRoot.OnSerializedMethod"); }
    
    }
    [Serializable]
    class SerMember : ISerializable
    {
        string text;
        public SerMember() 
        { 
            System.Console.WriteLine("SerMember.ctor");
            text = "test";
        }
        protected SerMember(SerializationInfo info, StreamingContext context) 
        {
            System.Console.WriteLine("SerMember.ctor(info, context)");
            text = info.GetString("text");
        }
        public void GetObjectData(SerializationInfo info, StreamingContext context) 
        { 
            System.Console.WriteLine("SerMember.GetObjectData");
            info.AddValue("text", text);
        }
        [OnDeserializing()]
        internal void OnDeserializingMethod(StreamingContext context) { System.Console.WriteLine("SerMember.OnDeserializingMethod"); }
    
        [OnDeserialized()]
        internal void OnDeserializedMethod(StreamingContext context) { System.Console.WriteLine("SerMember.OnDeserializedMethod"); }
    
        [OnSerializing()]
        internal void OnSerializingMethod(StreamingContext context) { System.Console.WriteLine("SerMember.OnSerializingMethod"); }
    
        [OnSerialized()]
        internal void OnSerializedMethod(StreamingContext context) { System.Console.WriteLine("SerMember.OnSerializedMethod"); }
    
    }
    
    0 讨论(0)
提交回复
热议问题