反序列化时,C#类中的字段初始化程序不运行
我有一个定义受保护字段的类。 受保护的字段具有字段初始值设定项。
当我反序列化具体类时,不会运行字段初始值设定项。 为什么? 解决问题的最佳模式是什么? 如果我将初始化移动到构造函数中,也不会调用构造函数。
[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 } }
原始阶层等级
[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); } }
支持方法
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; } }
在反序列化时, 既不调用构造函数也不调用字段初始值设定项,而是使用“空白”未初始化对象。
要解决它,您可以使用OnDeserializing
或OnDerserialized
属性让反序列化器调用具有以下签名的函数:
void OnDeserializing(System.Runtime.Serialization.StreamingContext c);
在该函数中,您可以初始化反序列化过程中遗漏的任何内容。
在约定方面,我倾向于让我的构造函数调用一个方法OnCreated()
,然后还有deserializating方法调用相同的东西。 然后,您可以在那里处理所有字段初始化,并确保在反序列化之前触发它。
[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; } } }
另一种方法是通过受保护的(在您的示例中)属性访问您的字段,并使用null-coalescing( ??
)运算符初始化该字段
protected Dictionary myDict = new Dictionary(); protected Dictionary MyDict { get { return myDict ?? (myDict = new Dictionary()); } }
缺点是你失去了readonly
的好处,并且需要确保你只通过属性访问该值。