views:

344

answers:

4

I have several classes that are immutable once their initial values are set. Eric Lippert calls this write-once immutability.

Implementing write-once immutability in C# usually means setting the initial values via the constructor. These values initialize readonly fields.

But if you need to serialize a class like this to XML, using either the XmlSerializer or the DataContractSerializer, you must have a parameterless constructor.

Does anyone have suggestions for how to work around this problem? Are there other forms of immutability that work better with serialization?

EDIT: As @Todd pointed out, the DataContractSerializer does not require a parameterless constructor. According to the DataContractSerializer documentation on MSDN, DataContractSerializer "does not call the constructor of the target object."

+2  A: 

Assuming this is your "immutable" object :

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; }
}

You can create a dummy class to represent that immutable object during serialization/deserialization :

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);
    }
}

When you have a property of type Immutable, don't serialize it, and instead serialize a 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; }
}

OK, this is a bit long for something that looks so simple... but it should work ;)

Thomas Levesque
+2  A: 

"Realio-trulio" immutability involves the constructor. Popsicle immutability is where you can do, for example:

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

(or the same with an object initializer)

This fits DataContractSerializer quite well, as long as you handle to on-serialized callback to do the Freeze. XmlSerializer doesn't do serialization callbacks, so is more work.

Either would suit if you use custom serialization (IXmlSerializable), though. Likewise, custom serialization is broadly doable with realio-trulio immutability, but is painful - and it is a bit of a lie, as it is "create once, then call interface method" - so not really properly immutable.

For true immutability, use a DTO.

Marc Gravell
A: 

I just had a look at the article you linked to. In his terminology, objects using readonly fields initialized in the constructor are called "write-only immutability".

"Popsicle immutability" is a bit different. Lippert gives two examples of where it would be useful: deserialization (the problem which you are trying to solve), and circular references where A and B need to be created independently but A needs have a reference to B, and B a reference to A.

The two more obvious ways to achieve this result have been mentioned here (as I was writing this, in fact). The pattern Thomas Levesque mentions is basically the "Builder" pattern. But it's rather unwieldly in this case because you need to not only go from Builder to Immutable, but also from Immutable to Builder so that the serialization/deserialization is symmetrical.

So the "lock" pattern, as mentioned by Marc Gravell, seems more useful. I'm not familiar with C# though, so I'm not sure how best to implement it. I guess probably a private property such as locked. Then all the getter methods need to explicitly check whether the object is locked (aka "frozen") yet. If so they should throw an exception (it's a runtime error; you cannot enforce popstick immutability at compile time).

Todd Owen
Thanks, Todd. I've corrected my question to read "write-once" immutability. I'll investigate using a Freeze method with DataContractSerializer.
dthrasher
Have a look at my other (second) answer. If DataContractSerializer can deserialize private members then you shouldn't need "popsicle immutability" or a Freeze() method at all.
Todd Owen
+1  A: 

I recommend taking a look at the discussion here: Why XML-Serializable class need a parameterless constructor.

You should consider using DataContractSerializer. As far as I can tell from the docs, this do not require a parameterless constructor, and can serialize/deserialize private members.

Todd Owen
You're right, Todd. According to MSDN, the DataContractSerializer does not call the constructor: http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx
dthrasher