El inicializador de campo en la clase C # no se ejecuta cuando se deserializa

Tengo una clase que define un campo protegido. El campo protegido tiene un inicializador de campo.

Cuando deserializo la clase concreta, el inicializador de campo no se ejecuta. ¿Por qué? ¿Cuál es el mejor patrón para resolver el problema? Si muevo la inicialización a un constructor, el constructor tampoco se invoca.

[DataContract] public class MyConcrete { // FIELD INITIALIZER DOES NOT RUN WHEN COMMENTED IN: protected readonly Dictionary myDict;// = new Dictionary(); public MyConcrete() { myDict = new Dictionary(); } private bool MyMethod(int key) { return myDict.ContainsKey(key); } private int myProp; [DataMember] public int MyProp { get { return myProp; } set { bool b = MyMethod(value); myProp = value; } // Call MyMethod to provoke error } } 

JERARQUÍA DE CLASE ORIGINAL

 [DataContract] public abstract class MyAbstract { // THIS INITIALIZER IS NOT RUN WHILE DESERIALIZING: protected readonly Dictionary myDict = new Dictionary(); private bool MyMethod(int key) { return myDict.ContainsKey(key); } private int myProp; [DataMember] public int MyProp { get { return myProp; } set { bool b = MyMethod(value); myProp = value; } // Call MyMethod to provoke error } } [DataContract] public class MyConcrete : MyAbstract { } class Program { static void Main(string[] args) { string tempfn = Path.GetTempFileName(); MyConcrete concrete = new MyConcrete() { MyProp = 42 }; string data = concrete.SerializeToString(); MyConcrete rehydrated = SerializationHelper.DeserializeFromString(data); } } 

MÉTODOS DE APOYO

 static public string SerializeToString(this T obj) { return SerializationHelper.SerializeToString(obj); } static public string SerializeToString(T obj) { DataContractSerializer s = new DataContractSerializer(typeof(T)); using (MemoryStream ms = new MemoryStream()) { s.WriteObject(ms, obj); ms.Position = 0; using (StreamReader sr = new StreamReader(ms)) { string serialized = sr.ReadToEnd(); return serialized; } } } static public T DeserializeFromString(string serializedDataAsString) { DataContractSerializer s = new DataContractSerializer(typeof(T)); using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(serializedDataAsString))) { object s2 = s.ReadObject(ms); return (T)s2; } } 

En la deserialización, ni los constructores ni los inicializadores de campo son llamados y en su lugar se utiliza un objeto no inicializado “en blanco”.

Para resolverlo, puede utilizar los OnDeserializing o OnDerserialized para hacer que el deserializador llame a una función con la siguiente firma:

 void OnDeserializing(System.Runtime.Serialization.StreamingContext c); 

En esa función es donde puedes inicializar lo que se haya perdido en el proceso de deserialización.

En términos de convención, tiendo a hacer que mi constructor llame a un método OnCreated() y luego también haga que el método de deserialización llame a lo mismo. A continuación, puede manejar toda la inicialización de campo allí y asegurarse de que se active antes de la deserialización.

 [DataContract] public abstract class MyAbstract { protected readonly Dictionary myDict; protected MyAbstract() { OnCreated(); } private void OnCreated() { myDict = new Dictionary(); } [OnDeserializing] private void OnDeserializing(StreamingContext c) { OnCreated(); } private bool MyMethod(int key) { return myDict.ContainsKey(key); } private int myProp; [DataMember] public int MyProp { get { return myProp; } set { bool b = MyMethod(value); myProp = value; } } } 

Otro enfoque es acceder a su campo a través de una propiedad protegida (en su ejemplo), e inicializar el campo utilizando el operador nulo-coalescente ( ?? )

 protected Dictionary myDict = new Dictionary(); protected Dictionary MyDict { get { return myDict ?? (myDict = new Dictionary()); } } 

Las desventajas son que usted pierde los beneficios de readonly , y necesita asegurarse de que solo acceda al valor a través de la propiedad.