99

我刚刚意识到一些疯狂的事情,我认为这是完全不可能的:反序列化对象时,DataContractSerializer 不会调用构造函数

以这门课为例:

[DataContract]
public class Book
{
    public Book()
    { // breakpoint here
    }

    [DataMember(Order = 0)]
    public string Title { get; set; }
    [DataMember(Order = 1)]
    public string Author { get; set; }
    [DataMember(Order = 2)]
    public string Summary { get; set; }
}

当我反序列化该类的对象时,没有命中断点。我完全不知道这怎么可能,因为它是这个对象的唯一构造函数!

我假设编译器可能因为该DataContract属性而生成了一个额外的构造函数,但我无法通过反射找到它......

所以,我想知道的是:如何在不调用构造函数的情况下创建我的类的实例?

注意:我知道我可以OnDeserializing在反序列化开始时使用该属性来初始化我的对象,这不是我的问题的主题。

4

5 回答 5

135

DataContractSerializer(like BinaryFormatter) 不使用任何构造函数。它将对象创建为空内存。

例如:

    Type type = typeof(Customer);
    object obj = System.Runtime.Serialization.
        FormatterServices.GetUninitializedObject(type);

假设是反序列化过程(或必要时的回调)将完全初始化它。

于 2009-07-02T21:17:17.300 回答
3

如果没有这种行为,有些情况是不可能的。想一想:

1)您有一个对象,该对象具有一个构造函数,该构造函数将新实例设置为“已初始化”状态。然后在该实例上调用一些方法,使其处于“已处理”状态。您不想创建具有“已处理”状态的新对象,但您仍然希望对实例进行反序列化/反序列化。

2) 您创建了一个具有私有构造函数和一些静态属性的类来控制一小组允许的构造函数参数。现在您仍然可以序列化/反序列化它们。

XmlSerializer 具有您预期的行为。我在使用 XmlSerializer 时遇到了一些问题,因为它确实需要一个默认构造函数。与此相关的是,有时拥有私有财产设置者是有意义的。但是 XmlSerializer 还需要属性上的公共 getter 和 setter 才能序列化/反序列化。

我想到了 DataContractSerializer / BinaryFormatter 行为,例如在序列化期间暂停实例的状态并在反序列化期间恢复。换句话说,实例不是“构造”而是“恢复”到更早的状态。

正如您已经提到的,[OnDeserializing] 属性可以使非序列化数据保持同步。

于 2012-10-19T21:49:39.423 回答
1

FWIW,您可以从 [OnDeserializing] 方法显式调用构造函数:

[OnDeserializing]
public void OnDeserializing(StreamingContext context)
{
    this.GetType().GetConstructor(System.Array.Empty<Type>()).Invoke(this, null);
}
于 2021-01-07T22:34:58.653 回答
0

使用 [OnDeserialized] 属性来初始化您的属性。

// This method is called after the object
// is completely deserialized. Use it instead of the
// constructror.
[OnDeserialized]
void OnDeserialized(StreamingContext context)
{
    fullName = firstName + " " + lastName;
}

请参考微软指南: https ://docs.microsoft.com/en-us/dotnet/standard/serialization/serialization-guidelines

于 2020-07-08T04:28:02.140 回答
0

就我而言,我想创建一个对象以在锁定子句中使用。我尝试实现 IDeserializationCallback(不起作用,因为回调仅在分配属性运行)、[OnDeserialized](不起作用,与前面的原因相同)和 ISerializable(不起作用,因为类用 [DataContractAttribute ])。

我的解决方法是在使用 Interlocked.CompareExchange 之前初始化该字段。完成了一些不必要的工作,但至少现在我的字段在 DataContractSerializer 创建它时被初始化。

Interlocked.CompareExchange(ref _sync, new object(), null);
于 2020-09-18T11:36:58.390 回答