为什么XML-Serializable类需要无参数构造函数

Mor*_*eng 168 .net c# xml-serialization

我正在编写代码来进行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();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果参数是没有无参数构造函数的类的实例,它将抛出异常.

未处理的异常:System.InvalidOperationException:CSharpConsole.Foo无法序列化,因为它没有无参数构造函数.System.Xml.Serialization.ModelScope.GetTypeModel的System.Xml.Serialization.TypeScope.GetTypeDesc(Type type,MemberInfo sourc e,Boolean directReference,Boolean throwOnError)中的System.Xml.Serialization.TypeDesc.CheckSupported()处于类型类型, System.Xml.Serialization上的System.Xml.Serialization.XmlSerializer..ctor(Type type,String defaultName space)中System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type,XmlRootAttribute root,String defaultNamespace)的布尔直接引用. XmlSerializer..ctor(类型类型)

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

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

cfe*_*uke 236

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

你可以制作你的构造函数private,internal如果你想要的话,只要它是无参数的.

  • 可访问性提示是一个很好的,但您的解释对序列化没有意义.只需要为反序列化创建对象.我猜测类型检查代码是内置在XmlSerializer构造函数中的,因为可以双向使用单个实例. (13认同)
  • @jwg一个例子是当您将XML发送到某种Web服务并且不想在您自己的组件中接收这些对象时. (7认同)
  • 请记住,即使您创建无参数构造函数`private`或`internal`,所有其值已序列化的属性也必须具有`public` setter. (5认同)
  • 是的,我经常这样做,虽然我已经接受了公共无参数构造函数很好,因为它们允许你使用带有泛型和新初始化语法的"new()".对于参数化构造函数,请使用静态工厂方法或构建器模式实现. (2认同)

Mar*_*ell 73

这是一个限制XmlSerializer.请注意,BinaryFormatterDataContractSerializer 没有要求这一点-他们可以出醚创建一个初始化的对象和反序列化过程中初始化.

由于您使用的是xml,因此可以考虑使用/ ] 来DataContractSerializer标记您的类,但请注意,这会更改模式(例如,没有相应的- 所有内容都成为元素).[DataContract][DataMember[XmlAttribute]

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

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

我有自己的序列化引擎,但我不打算使用它FormatterServices; 我非常想知道构造函数(任何构造函数)实际执行过.

  • 嘿; 事实证明我不遵循自己的建议; protobuf-net(可选)允许`FormatterServices`用于*年龄* (6认同)

Mik*_*kis 11

答案是:没有任何充分的理由。

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

您的类未能通过的检查是仅与反序列化相关的检查之一。发生的情况如下:

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

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

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

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

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

因此,如果该类XmlSerializer以仅执行与序列化相关的检查的方式编写,那么您的类将通过,因为序列化绝对不需要有一个无参数构造函数。

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

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

  • 哈哈,您说,“没有任何充分的理由”,然后继续说,“XmlSerializer 无法调用除无参数构造函数之外的任何构造函数,因为它不知道将哪些参数传递给接受参数的构造函数。”如果它不知道要传递给构造函数什么参数,那么它如何知道要传递给工厂什么参数呢?或者用哪个工厂?我无法想象这个工具使用起来更简单 - 您想要反序列化一个类,然后让反序列化器创建一个默认实例,然后填充您标记的每个字段。简单的。 (2认同)