Подтвердить что ты не робот

.NET Deserialisation с OnDeserializing и OnDeserialized

Я использую простой класс, который можно сериализовать. Он имеет конструктор для десериализации:

protected MyClass(SerializationInfo info, StreamingContext context)

и метод GetObjectData для сериализации. Он отлично работает.

Теперь я добавил два метода для контроля десериализации:

        [OnDeserializing()]
    internal void OnDeserializingMethod(StreamingContext context)
    {
        System.Diagnostics.Trace.WriteLine("OnDeserializingMethod: " + this.GetType().ToString());
    }

    [OnDeserialized()]
    internal void OnDeserializedMethod(StreamingContext context)
    {
        System.Diagnostics.Trace.WriteLine("OnDeserializedMethod: " + this.GetType().ToString());
    }

и задавался вопросом, в каком порядке эти методы называются. Теперь оба метода вызываются до вызова конструктора. Как это возможно, и почему не вызван метод OnDeserialized после того, как был вызван конструктор (deserialization-)? И как можно вызывать (нестатический) метод до выполнения любого конструктора? (Я использую BinaryFormatter)

4b9b3361

Ответ 1

Теперь оба метода вызываются до того, как конструктор будет вызван

Нет, порядок:

  • OnDeserializingMethod
  • .ctor
  • OnDeserializedMethod

И как можно вызвать (нестатический) метод до выполнения любого конструктора?

Потому что он обманывает и лжет; он не создает объект с помощью конструктора; нет - действительно. Он использует FormatterServices.GetUninitializedObject для выделения пустотного пространства ванили. И тогда, если есть собственный конструктор десериализации, он вызывает конструктор над вершиной этого объекта. Насти. Как это, в основном:

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, вероятно, они должны были сделать этот второй метод на интерфейсе ISerializable, но по какой-то причине: они этого не сделали. Позор действительно: это сделало бы его более честным и избегало бы людей, которые должны были помнить о реализации пользовательского конструктора.

Пример вывода:

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

Пример кода:

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");
        }
    }
}

Ответ 2

Порядок вызовов зависит от того, является ли объект корнем сериализованного дерева или каким-либо членом объекта, который также сериализуется в одном и том же графе объектов. Я получаю следующий вывод с расширенным примером, представленным Марк Гравелл:

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

Обратите внимание, что при десериализации SerMember.ctor вызывается после SerMember.OnDeserializedMethod! Это код:

        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"); }

}

Ответ 3

[OnDeserializing] Указывает метод, который будет вызываться непосредственно перед десериализацией [OnDeserialized] Указывает метод, который будет вызываться сразу после десериализации

Метод [OnDeserializing] действует как псевдоконструктор для десериализации и это полезно для инициализации полей, исключенных из сериализации:

[OnDeserializing] и [OnDeserialized] Deserialization обходит все ваши обычные конструкторы, а также инициализаторы полей. Это не имеет большого значения, если каждое поле участвует в сериализации, но это может быть проблематично, если некоторые поля исключаются через [NonSerialized].

Я взял этот текст из книги Albahari С# 5.0 в двух словах на странице 713, проверив его в Интернете много примеров и описание вашей проблемы.

Спасибо