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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/267724/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 20:34:18  来源:igfitidea点击:

Why XML-Serializable class need a parameterless constructor

c#.netxml-serialization

提问by Morgan Cheng

I'm writing code to do Xml serialization. With below function.

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

If the argument is a instance of class without parameterless constructor, it will throw a exception.

如果参数是没有无参数构造函数的类的实例,则会抛出异常。

Unhandled Exception: System.InvalidOperationException: CSharpConsole.Foo cannot be serialized because it does not have a parameterless constructor. at System.Xml.Serialization.TypeDesc.CheckSupported() at System.Xml.Serialization.TypeScope.GetTypeDesc(Type type, MemberInfo sourc e, Boolean directReference, Boolean throwOnError) at System.Xml.Serialization.ModelScope.GetTypeModel(Type type, Boolean direct Reference) at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type , XmlRootAttribute root, String defaultNamespace) at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultName space) at System.Xml.Serialization.XmlSerializer..ctor(Type type)

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

Why must there be a parameterless constructor in order to allow xml serialization to succeed?

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

EDIT: thanks for cfeduke's answer. The parameterless constructor can be private or internal.

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

采纳答案by cfeduke

During an object's de-serialization, the class responsible for de-serializing an object creates an instance of the serialized class and then proceeds to populate the serialized fields and properties only after acquiring an instance to populate.

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

You can make your constructor privateor internalif you want, just so long as it's parameterless.

你可以创建你的构造函数,private或者internal如果你愿意,只要它是无参数的。

回答by Dmitry Khalatov

First of all, this what is written in documentation. I think it is one of your class fields, not the main one - and how you want deserialiser to construct it back w/o parameterless construction ?

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

I think there is a workaround to make constructor private.

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

回答by Marc Gravell

This is a limitation of XmlSerializer. Note that BinaryFormatterand DataContractSerializerdo notrequire this - they can create an uninitialized object out of the ether and initialize it during deserialization.

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

Since you are using xml, you might consider using DataContractSerializerand marking your class with [DataContract]/[DataMember], but note that this changes the schema (for example, there is no equivalent of [XmlAttribute]- everything becomes elements).

由于您使用XML,你可能会考虑使用DataContractSerializer和标记使用你的类[DataContract]/ [DataMember],但注意这改变的模式(例如,不存在等价物[XmlAttribute]-一切都变得元素)。

Update: if you really want to know, BinaryFormatteret al use FormatterServices.GetUninitializedObject()to create the object without invoking the constructor. Probably dangerous; I don't recommend using it too often ;-p See also the remarks on MSDN:

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

Because the new instance of the object is initialized to zero and no constructors are run, the object might not represent a state that is regarded as valid by that object. The current method should only be used for deserialization when the user intends to immediately populate all fields. It does not create an uninitialized string, since creating an empty instance of an immutable type serves no purpose.

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

I have my ownserialization engine, but I don't intend making it use FormatterServices; I quite like knowing that a constructor (anyconstructor) has actually executed.

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

回答by Mike Nakis

The answer is: for no good reason whatsoever.

答案是:没有任何理由。

Contrary to its name, the XmlSerializerclass is used not only for serialization, but also for deserialization. It performs certain checks on your class to make sure that it will work, and some of those checks are only pertinent to deserialization, but it performs them all anyway, because it does not know what you intend to do later on.

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

The check that your class fails to pass is one of the checks that are only pertinent to deserialization. Here is what happens:

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

  • During deserialization, the XmlSerializerclass will need to create instances of your type.

  • In order to create an instance of a type, a constructor of that type needs to be invoked.

  • If you did not declare a constructor, the compiler has already supplied a default parameterless constructor, but if you did declare a constructor, then that's the only constructor available.

  • So, if the constructor that you declared accepts parameters, then the only way to instantiate your class is by invoking that constructor which accepts parameters.

  • However, XmlSerializeris not capable of invoking any constructor except a parameterless constructor, because it does not know what parameters to pass to constructors that accept parameters. So, it checks to see if your class has a parameterless constructor, and since it does not, it fails.

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

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

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

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

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

So, if the XmlSerializerclass had been written in such a way as to only perform the checks pertinent to serialization, then your class would pass, because there is absolutely nothing about serialization that makes it necessary to have a parameterless constructor.

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

As others have already pointed out, the quick solution to your problem is to simply add a parameterless constructor. Unfortunately, it is also a dirty solution, because it means that you cannot have any readonlymembers initialized from constructor parameters.

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

In addition to all this, the XmlSerializerclass couldhave 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.

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