23

我使用一个可序列化的简单类。它有一个反序列化的构造函数:

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”方法?在执行任何构造函数之前如何调用(非静态)方法?(我正在使用 BinaryFormatter)

4

3 回答 3

26

现在这两个方法都在构造函数被调用之前被调用

不,顺序是:

  • 反序列化方法
  • .ctor
  • 反序列化方法

在执行任何构造函数之前如何调用(非静态)方法?

因为它欺骗和撒谎;它不使用构造函数创建对象;不完全是。它用于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");
        }
    }
}
于 2013-08-22T10:06:38.263 回答
4

调用的顺序取决于对象是序列化树的根还是对象的某个成员,该成员也在同一个对象图中被序列化。通过 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

请注意,在反序列化中 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"); }

}
于 2013-08-22T11:39:49.693 回答
2

[OnDeserializing] 表示要在反序列化之前调用的方法 [OnDeserialized] 表示要在反序列化之后调用的方法

[OnDeserializing] 方法充当反序列化的伪构造函数,它对于初始化从序列化中排除的字段很有用:

[OnDeserializing] 和 [OnDeserialized] 反序列化绕过所有正常的构造函数以及字段初始值设定项。如果每个字段都参与序列化,这没什么影响,但如果某些字段通过 [NonSerialized] 排除,则可能会出现问题。

我从 Albahari book C# 5.0 in nutshell page 713 中获取了这段文字,在网上查看了很多关于您的问题的示例和描述。

谢谢

于 2013-08-22T09:59:54.920 回答