【问题标题】:Why XML-Serializable class need a parameterless constructor为什么 XML-Serializable 类需要无参数构造函数
【发布时间】:2010-09-21 00:43:20
【问题描述】:

我正在编写代码来进行 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(类型 类型、成员信息源、布尔值 directReference,布尔型 throwOnError) 在 System.Xml.Serialization.ModelScope.GetTypeModel(类型 类型,布尔直接引用)在 System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(类型 类型,XmlRootAttribute 根,字符串 默认命名空间)在 System.Xml.Serialization.XmlSerializer..ctor(类型 类型,字符串默认名称空间)在 System.Xml.Serialization.XmlSerializer..ctor(类型 类型)

为什么一定要有无参构造函数才能让xml序列化成功?

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

【问题讨论】:

  • 如果您有兴趣,我发现了如何在不需要构造函数的情况下创建对象(请参阅更新)——但这对 XmlSerializer 毫无帮助——它仍然需要它。也许对自定义代码有用。
  • XmlSerializer 需要一个默认的无参数构造函数进行反序列化。
  • 无参数构造函数的声明不是必需的。一旦声明了参数化构造函数,也必须声明无参数构造函数。

标签: c# .net xml-serialization


【解决方案1】:

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

您可以根据需要将构造函数设为privateinternal,只要它是无参数的即可。

【讨论】:

  • 哦,所以,我可以将无参数 ctor 设为私有或内部,并且序列化仍然有效。谢谢你的回答。
  • 是的,我经常这样做,尽管我已经接受公共无参数构造函数很棒,因为它们允许您将“new()”与泛型和新的初始化语法一起使用。对于带参数的构造函数,使用静态工厂方法或构建器模式实现。
  • 无障碍提示是一个很好的提示,但您的解释对序列化没有意义。仅需要为反序列化创建对象。我猜测类型检查代码是内置在 XmlSerializer 构造函数中的,因为单个实例可以两种方式使用。
  • @jwg 一个例子是,当您将 XML 发送到某种 Web 服务并且对在自己的组件中接收这些对象不感兴趣时​​。
  • 请记住,即使您将无参数构造函数设为privateinternal,其值已序列化的所有属性也必须具有public 设置器。
【解决方案2】:

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

由于您使用的是 xml,您可能会考虑使用 DataContractSerializer 并使用 [DataContract]/[DataMember] 标记您的类,但请注意这会更改架构(例如,不存在 [XmlAttribute] 的等价物 -一切都变成了元素)。

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

因为对象的新实例 被初始化为零并且没有 构造函数运行时,对象可能 不代表被视为的状态 对该对象有效。目前的 方法只能用于 用户想要的反序列化 立即填充所有字段。它 不会创建未初始化的 字符串,因为创建一个空 不可变类型的实例服务 没有目的。

我有我的own 序列化引擎,但我不打算让它使用FormatterServices;我很想知道构造函数(any 构造函数)已经实际执行了。

【讨论】:

  • 感谢有关 FormatterServices.GetUninitializedObject(Type) 的提示。 :)
  • 嘿;原来我没有听从自己的建议; protobuf-net 已(可选)允许 FormatterServices 使用 ages
  • 但是我不明白的是,在没有指定构造函数的情况下,编译器会创建一个公共的无参数构造函数。那么为什么 xml 反序列化引擎还不够好呢?
  • 如果我想反序列化 XML 并使用它们的构造函数初始化某些对象(以便通过构造函数提供元素/属性),有什么方法可以实现吗?有没有办法自定义序列化过程,以便它使用它们的构造函数来构建对象?
  • @Shimmy 不;不支持。 IXmlSerializable,但是a:发生在构造函数之后,并且b:它非常丑陋且难以正确处理(尤其是反序列化)-我强烈建议不要试图实现它,但是:它不允许你使用构造函数
【解决方案3】:

答案是:没有任何理由。

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

您的类未能通过的检查是仅与反序列化相关的检查之一。这是发生了什么:

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

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

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

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

  • 但是,XmlSerializer 不能调用任何构造函数 除了无参数构造函数,因为它不知道什么 传递给接受参数的构造函数的参数。因此,它会检查您的类是否具有无参数构造函数,如果没有,则失败。

因此,如果 XmlSerializer 类的编写方式仅执行与序列化相关的检查,那么您的类将通过,因为序列化绝对没有任何必要使用无参数构造函数.

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

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

【讨论】:

  • 大声笑,For no good reason whatsoever, 然后继续说,XmlSerializer is not capable of invoking any constructor except a parameterless constructor, because it does not know what parameters to pass to constructors that accept parameters. 如果它不知道传递给构造函数的参数,那么它怎么知道传递给工厂的参数?或者使用哪个工厂?我无法想象这个工具使用起来更简单——你想要一个反序列化的类,然后让反序列化器创建一个默认实例,然后填充你标记的每个字段。容易。
  • @Chuck 你可能想问另一个 StackOverflow 问题“工厂方法如何帮助我处理构造函数参数”并告诉我,我会为你解答。
【解决方案4】:

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

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

【讨论】:

    猜你喜欢
    • 2012-09-15
    • 2011-02-25
    • 1970-01-01
    • 2015-10-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多