为什么 XML 可序列化类需要无参数构造函数

我正在编写代码来进行 XML 序列化。

public static string SerializeToXml(object obj)
{
XmlSerializer serializer = new XmlSerializer(obj.GetType());
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, obj);
return writer.ToString();
}
}

如果参数是没有无参数构造函数的类的实例,它将引发异常。

例外: 例外情况: 无法序列化 因为它没有 无参数构造函数 System.Xml.Serialization. TypeDesc. CheckSupport () 在 序列化.TypeScope. GetTypeDesc (类型 类型,MemberInfo source e,Boolean DirectReference,Boolean throwOnError) 在 序列化.ModelScope.GetTypeModel (类型 类型,布尔直接引用) 系统。 Xml.Serialization Type,XmlRootAttribute 根,String 默认命名空间) 序列化.XmlSerializer. . ctor (类型 类型,String defaultName 空间) 序列化.XmlSerializer. . ctor (类型 类别)

为什么必须有一个无参数构造函数才能使 xml 序列化成功?

编辑: 感谢 cfeduk 的回答。无参数构造函数可以是私有的,也可以是内部的。

135781 次浏览

在对象的反序列化过程中,负责反序列化对象的类创建了序列化类的实例,然后只有在获取要填充的实例之后才继续填充序列化字段和属性。

如果愿意,可以将构造函数设置为 privateinternal,只要它是无参数的。

首先,这是用 文件写的。我认为它是您的类字段之一,而不是主字段-您希望反序列化器如何构造它回 w/o 无参数构造?

我认为有一个变通方法可以使构造函数私有化。

这是 XmlSerializer的一个限制。请注意,BinaryFormatterDataContractSerializer 不要需要这样做-它们可以在以太中创建未初始化的对象,并在反序列化期间初始化它。

因为您使用的是 xml,所以可以考虑使用 DataContractSerializer并使用 [DataContract]/[DataMember]来标记类,但是请注意,这会改变模式(例如,没有相当于 [XmlAttribute]的东西-所有东西都变成了元素)。

更新: 如果您真的想知道,BinaryFormatter等人使用 FormatterServices.GetUninitializedObject()创建对象,而不调用构造函数。可能很危险; 我不建议经常使用它;-p 参见 MSDN 上的评论:

因为对象的新实例 初始化为零和否 构造函数运行时,对象可能 不代表被认为是 作为该对象的有效值。当前 方法只应用于 当用户需要时进行反序列化 立即填充所有字段 不会创建未初始化的 字符串,因为创建了一个空的 不可变类型的实例服务 没有目的。

我有我的 自己的序列化引擎,但是我不打算使用 FormatterServices; 我非常喜欢知道一个构造函数(任何构造函数)实际上已经执行了。

答案是: 没有任何好的理由

与其名称相反,XmlSerializer类不仅用于序列化,还用于反序列化。它对您的类执行某些检查以确保它能够工作,其中一些检查只与反序列化有关,但它还是执行了所有检查,因为它不知道您以后打算做什么。

类未能通过的检查是仅与反序列化相关的检查之一。事情是这样的:

  • 在反序列化过程中,XmlSerializer类将需要创建 类型的实例。

  • 为了创建类型的实例,该类型的构造函数 需要被调用

  • 如果没有声明构造函数,则编译器已经 提供了一个默认的无参数构造函数,但是如果您确实声明了 一个构造函数,那么这就是唯一可用的构造函数。

  • 因此,如果您声明的构造函数接受参数,那么 实例化类的唯一方法是调用该构造函数 它接受参数。

  • 但是,XmlSerializer不能调用任何构造函数吗 除了一个无参数的构造函数,因为它不知道 参数传递给接受参数的构造函数。因此,它检查类是否有无参数构造函数,由于没有,所以它失败了。

因此,如果 XmlSerializer类的编写方式只是为了执行与序列化相关的检查,那么您的类就会通过,因为序列化绝对不需要使用无参数构造函数。

正如其他人已经指出的,快速解决问题的方法是简单地添加一个无参数构造函数。不幸的是,这也是一个糟糕的解决方案,因为这意味着不能从构造函数参数初始化任何 readonly成员。

除此之外,XmlSerializer可以的编写方式甚至允许在没有无参数构造函数的情况下反序列化类。只需要利用 “工厂方法设计模式”(Wikipedia)就可以了。从表面上看,微软认为这种设计模式对于 DotNet 程序员来说太先进了,他们显然不应该不必要地与这些东西混淆。因此,根据微软的说法,DotNet 程序员应该更好地坚持使用无参数构造函数。

似乎没有人真正阅读原文... ... 它是关于序列化而不是 DE-... ... 为此,根本不需要构造函数或调用构造函数。问题只是微软糟糕的编码实践。