c# xml-serialization immutability

c# - Inmutabilidad y serialización XML



xml-serialization immutability (5)

Acabo de echar un vistazo al artículo al que has vinculado. En su terminología, los objetos que usan campos de solo lectura inicializados en el constructor se denominan "inmutabilidad de solo escritura".

La "inmutabilidad de la paleta" es un poco diferente. Lippert da dos ejemplos de dónde sería útil: deserialización (el problema que está tratando de resolver) y referencias circulares donde A y B deben crearse independientemente pero A necesita tener una referencia a B, y B una referencia a A .

Las dos formas más obvias de lograr este resultado se han mencionado aquí (de hecho, cuando estaba escribiendo esto). El patrón que menciona Thomas Levesque es básicamente el patrón "Constructor". Pero en este caso es bastante inoportuno porque no solo debe pasar de Builder a Immutable, sino también de Immutable a Builder para que la serialización / deserialización sea simétrica.

Así que el patrón de "bloqueo", como lo menciona Marc Gravell, parece más útil. Sin embargo, no estoy familiarizado con C #, así que no estoy seguro de cuál es la mejor manera de implementarlo. Supongo que probablemente una propiedad privada como encerrada . Entonces, todos los métodos de obtención deben verificar explícitamente si el objeto está bloqueado (también conocido como "congelado") todavía. Si es así, deberían lanzar una excepción (es un error en el tiempo de ejecución; no se puede forzar la inmutabilidad del popstick en el momento de la compilación).

Tengo varias clases que son inmutables una vez que se establecen sus valores iniciales. Eric Lippert llama a esto inmutabilidad de escritura única .

Implementar la inmutabilidad de una sola escritura en C # generalmente significa establecer los valores iniciales a través del constructor. Estos valores inicializan los campos de solo lectura.

Pero si necesita serializar una clase como esta en XML, utilizando XmlSerializer o DataContractSerializer, debe tener un constructor sin parámetros.

¿Alguien tiene sugerencias sobre cómo solucionar este problema? ¿Hay otras formas de inmutabilidad que funcionan mejor con la serialización?

EDITAR: Como lo señaló @Todd, el DataContractSerializer no requiere un constructor sin parámetros. De acuerdo con la documentación de DataContractSerializer en MSDN , DataContractSerializer "no llama al constructor del objeto de destino".


Asumiendo que este es su objeto "inmutable":

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

Puede crear una clase ficticia para representar ese objeto inmutable durante la serialización / deserialización:

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

Cuando tenga una propiedad de tipo Inmutable, no la serialice, y en su lugar serialice un 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, esto es un poco largo para algo que parece tan simple ... pero debería funcionar;)


La inmutabilidad "realio-trulio" involucra al constructor. La inmutabilidad de la paleta es donde puedes hacer, por ejemplo:

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

(o lo mismo con un inicializador de objeto)

Esto encaja bastante bien con DataContractSerializer , siempre y cuando maneje la devolución de llamada en serie para hacer el Freeze . XmlSerializer no hace devoluciones de llamada de serialización, por lo que es más trabajo.

Sin embargo, podría ser adecuado si utiliza una serialización personalizada ( IXmlSerializable ). Del mismo modo, la serialización personalizada es ampliamente factible con la inmutabilidad de realio-trulio, pero es dolorosa, y es un poco mentira, ya que es "crear una vez, luego llamar al método de interfaz", por lo que no es realmente inmutable.

Para una verdadera inmutabilidad, use un DTO.



Si la clase es realmente inmutable, solo use campos públicos de solo lectura marcados con atributos.

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