不可变性和XML序列化

dth*_*her 31 c# xml-serialization immutability

一旦设置了初始值,我就有几个不可变的类.Eric Lippert称之为一次性写入不可变性.

在C#中实现一次写入不变性通常意味着通过构造函数设置初始值.这些值初始化只读字段.

但是,如果需要使用XmlSerializer或DataContractSerializer将类似这样的类序列化为XML,则必须具有无参数构造函数.

有没有人建议如何解决这个问题?是否有其他形式的不变性可以更好地与序列化?

编辑:正如@Todd所指出的,DataContractSerializer不需要无参数构造函数.根据MSDN上的DataContractSerializer文档,DataContractSerializer"不会调用目标对象的构造函数."

Tho*_*que 11

假设这是你的"不可变"对象:

public class Immutable
{
    public Immutable(string foo, int bar)
    {
        this.Foo = foo;
        this.Bar = bar;
    }

    public string Foo { get; private set; }
    public int Bar { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

您可以创建一个虚拟类来表示序列化/反序列化期间的不可变对象:

public class DummyImmutable
{
    public DummyImmutable(Immutable i)
    {
        this.Foo = i.Foo;
        this.Bar = i.Bar;
    }

    public string Foo { get; set; }
    public int Bar { get; set; }

    public Immutable GetImmutable()
    {
        return new Immutable(this.Foo, this.Bar);
    }
}
Run Code Online (Sandbox Code Playgroud)

当你有一个类型为Immutable的属性时,不要序列化它,而是序列化DummyImmutable:

[XmlIgnore]
public Immutable SomeProperty { get; set; }

[XmlElement("SomeProperty")]
public DummyImmutable SomePropertyXml
{
    get { return new DummyImmutable(this.SomeProperty); }
    set { this.SomeProperty = value != null ? value.GetImmutable() : null; }
}
Run Code Online (Sandbox Code Playgroud)

好吧,对于看起来如此简单的东西来说,这有点长......但它应该有效;)

  • 这很讨厌,但似乎是微软推荐的方法. (2认同)

Mar*_*ell 10

"Realio-trulio"不变性涉及构造函数.冰棒不变性是你可以做的地方,例如:

Person p = new Person();
p.Name = "Fred";
p.DateOfBirth = DateTime.Today;
p.Freeze(); // **now** immutable (edit attempts throw an exception)
Run Code Online (Sandbox Code Playgroud)

(或与对象初始化程序相同)

DataContractSerializer非常适合,只要你处理on-serialized回调来做Freeze.XmlSerializer 这样做的序列化回调,所以更多的工作.

但是,如果您使用自定义序列化(IXmlSerializable),则适合.同样,自定义序列化是广泛使用realio-trulio不变性是可行的,但痛苦的-这是一个有点骗人的,因为它是"创造一次,然后调用接口方法" -所以不是真的正确一成不变的.

要获得真正的不变性,请使用DTO.


nie*_*ras 10

如果该类是真正不可变的,只需使用标记有属性的公共只读字段.

 [DataContract()]
 public class Immutable
 {
      [DataMember(IsRequired=true)]
      public readonly string Member;

      public Immutable(string member)
      {
           Member = member;
      }
 }
Run Code Online (Sandbox Code Playgroud)