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
"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.
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.
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).
If the class is truly immutable, just use public readonly fields marked with attributes.
[DataContract()]
public class Immutable
{
[DataMember(IsRequired=true)]
public readonly string Member;
public Immutable(string member)
{
Member = member;
}
}
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 ;)