2012-02-17 10 views
11

Necesito inicializar el campo de solo lectura privado después de la Deserialización. He folowing DataContract:Inicializar campos de solo lectura privados después de la Deserialización

[DataContract] 
public class Item 
{ 
    public Item() 
    { 
     // Constructor not called at Deserialization 
     // because of FormatterServices.GetUninitializedObject is used 
     // so field will not be initialized by constructor at Deserialization 
     _privateReadonlyField = new object(); 
    } 

    // Initialization will not be called at Deserialization (same reason as for constructor) 
    private readonly object _privateReadonlyField = new object(); 

    [DataMember] 
    public string SomeSerializableProperty { get; set; } 

    [OnDeserializing] 
    public void OnDeserializing(StreamingContext context) 
    { 
     // With this line code even not compiles, since readonly fields can be initialized only in constructor 
     _privateReadonlyField = new object(); 
    } 
} 

Todo lo que necesito, que después de Deserialización _privateReadonlyField no es nulo.

Cualquier sugerencia sobre esto - ¿es posible? O necesito eliminar la clave "readonly", que no es una buena opción.

+0

Qué método de serialización está usando? La construcción de los objetos difiere según los diferentes métodos. –

+0

¿Qué hay de malo en marcar su '_privateReadonlyField' a' [DataMember] '? El serializador de contrato de datos se encargaría sin problema. – dasblinkenlight

+0

Joachim Isaksson: Estoy usando DataContractJsonSerializer, pero en realidad esto no importa: todos los serializadores usan FormatterServices.GetUninitializedObject al deserializar. – Andris

Respuesta

7

Cualquier campo declarado como private readonly se puede instanciar en la misma línea donde fue declarado o dentro de un constructor. Una vez hecho esto, no se puede cambiar.

De MSDN:

La palabra clave de sólo lectura es un modificador que se puede utilizar en los campos. Cuando una declaración de campo incluye un modificador de solo lectura, las asignaciones a los campos introducidos por la declaración solo pueden ocurrir como parte de la declaración o en un constructor en la misma clase.

Eso significa que tendrá que eliminar la palabra clave readonly para que funcione.

+0

Gracias, Huske. Lamentablemente, usted aprobó mi opinión al respecto, esperaba que existiera alguna forma de resolver este problema. – Andris

8

La serialización puede leer los valores de los campos de solo lectura porque usa la reflexión, que ignora las reglas de accesibilidad. Se puede argumentar que la siguiente es, por lo tanto, se justifica como parte del proceso de serialización, a pesar de que no te recomiendo en contra de ella en casi cualquier otra circunstancia:

private readonly Doodad _oldField; 

[OptionalField(VersionAdded = 2)] 
private readonly Widget _newField; 

[OnDeserialized] 
private void OnDeserialized(StreamingContext context) 
{ 
    if (_oldField != null && _newField == null) 
    { 
     var field = GetType().GetField("_newField", 
      System.Reflection.BindingFlags.Instance | 
      System.Reflection.BindingFlags.DeclaredOnly | 
      System.Reflection.BindingFlags.NonPublic); 
     field.SetValue(this, new Widget(_oldField)); 
    } 
} 
+1

Gracias por su respuesta. Realmente me olvidé de la reflexión cuando le hice la pregunta. – Andris

+0

¿Qué efecto tiene el atributo '[OptionalField]' en esta solución? ¿'OnDeserialized' todavía funciona sin él? –

+0

Si mal no recuerdo, omitirlo hará que la deserialización arroje una excepción al leer una entrada que no contiene ese campo. –

Cuestiones relacionadas