175

我正在编写代码来进行 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.InvalidOperationException:CSharpConsole.Foo 无法序列化,因为它没有无参数构造函数。在 System.Xml.Serialization.TypeDesc.CheckSupported() 在 System.Xml.Serialization.TypeScope.GetTypeDesc(Type type, MemberInfo source, Boolean directReference, Boolean throwOnError) 在 System.Xml.Serialization.ModelScope.GetTypeModel(Type type,布尔直接引用)在 System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultName space) 在 System.Xml.Serialization 的 System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type , XmlRootAttribute root, String defaultNamespace)。 XmlSerializer..ctor(类型类型)

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

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

4

4 回答 4

252

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

您可以制作您的构造函数private,或者internal如果您愿意,只要它是无参数的即可。

于 2008-11-06T05:37:22.070 回答
77

这是一个限制XmlSerializer。请注意BinaryFormatter并且DataContractSerializer 不需要这样做- 他们可以从以太中创建一个未初始化的对象并在反序列化期间对其进行初始化。

由于您使用的是 xml,因此您可能会考虑使用/ ]DataContractSerializer并标记您的类,但请注意这会更改架构(例如,没有等效的- 所有内容都成为元素)。[DataContract][DataMember[XmlAttribute]

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

因为对象的新实例被初始化为零并且没有运行构造函数,所以该对象可能不代表该对象认为有效的状态。当前方法仅应在用户打算立即填充所有字段时用于反序列化。它不会创建未初始化的字符串,因为创建不可变类型的空实例没有任何意义。

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

于 2008-11-06T08:02:33.320 回答
4

答案是:没有任何理由。

与其名称相反,XmlSerializer该类不仅用于序列化,还用于反序列化。它对您的类执行某些检查以确保它能够正常工作,其中一些检查仅与反序列化有关,但无论如何它都会执行它们,因为它不知道您以后打算做什么。

您的课程未能通过的检查是仅与反序列化相关的检查之一。这是发生的事情:

  • 在反序列化期间,XmlSerializer该类将需要创建您的类型的实例。

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

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

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

  • 但是,XmlSerializer除了无参数构造函数之外,它不能调用任何构造函数,因为它不知道将哪些参数传递给接受参数的构造函数。所以,它会检查你的类是否有一个无参数的构造函数,因为它没有,所以它失败了。

因此,如果XmlSerializer该类的编写方式仅执行与序列化相关的检查,那么您的类将通过,因为绝对没有关于序列化的任何内容使得必须有一个无参数的构造函数。

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

In addition to all this, the XmlSerializer class could have been written in such a way as to allow even deserialization of classes without parameterless constructors. All it would take would be to make use of "The Factory Method Design Pattern" (Wikipedia). From the looks of it, Microsoft decided that this design pattern is far too advanced for DotNet programmers, who apparently should not be unnecessarily confused with such things. So, DotNet programmers should better stick to parameterless constructors, according to Microsoft.

于 2019-11-02T19:57:13.313 回答
0

首先,这是文档中所写的内容。我认为这是您的类字段之一,而不是主要字段-以及您希望反序列化器如何在没有无参数构造的情况下将其构造回来?

我认为有一种解决方法可以将构造函数设为私有。

于 2008-11-06T05:47:08.477 回答