我刚刚意识到一些疯狂的东西,我认为这是完全不可能的:当反序列化一个对象时, 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
属性来初始化我的对象,这不是我的问题的主题。
答案 0 :(得分:128)
DataContractSerializer
(如BinaryFormatter
)不使用任何构造函数。它将对象创建为空内存。
例如:
Type type = typeof(Customer);
object obj = System.Runtime.Serialization.
FormatterServices.GetUninitializedObject(type);
假设反序列化过程(或必要时的回调)将完全初始化它。
答案 1 :(得分:3)
如果没有这种行为,有些情况是不可能的。想一想以下内容:
1)你有一个对象有一个构造函数,它将新实例设置为“初始化”状态。然后在该实例上调用一些方法,使其处于“已处理”状态。您不希望创建具有“已处理”状态的新对象,但仍希望对该实例进行序列化/反序列化。
2)您创建了一个带有私有构造函数和一些静态属性的类来控制一小组允许的构造函数参数。现在你仍然可以序列化/反序列化它们。
XmlSerializer具有您期望的行为。我有一些XmlSerializer的问题,因为它需要一个默认的构造函数。与此相关,有时拥有私有属性设置器是有意义的。但是XmlSerializer还需要公共getter和setter属性才能序列化/反序列化。
我认为DataContractSerializer / BinaryFormatter的行为就像在序列化期间挂起实例的状态并在反序列化期间恢复一样。换句话说,实例不是“构造”的,而是“恢复”到较早的状态。
正如您已经提到的,[OnDeserializing]属性可以使非序列化数据保持同步。
答案 2 :(得分:1)
FWIW,您可以从 [OnDeserializing] 方法显式调用构造函数:
[OnDeserializing]
public void OnDeserializing(StreamingContext context)
{
this.GetType().GetConstructor(System.Array.Empty<Type>()).Invoke(this, null);
}
答案 3 :(得分: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;
}
请参考Microsoft指导方针: https://docs.microsoft.com/en-us/dotnet/standard/serialization/serialization-guidelines
答案 4 :(得分:0)
就我而言,我想创建一个在锁子句中使用的对象。我尝试实现IDeserializationCallback(无法正常工作,因为回调仅在 属性被分配后运行),[OnDeserialized](无法正常工作,与先前的原因相同)和ISerializable(因为类用[DataContractAttribute]装饰。)
我的解决方法是在使用Interlocked.CompareExchange之前初始化该字段。做了一些不必要的工作,但至少现在是在DataContractSerializer创建它的时候初始化了我的字段。
Interlocked.CompareExchange(ref _sync, new object(), null);