Инициализатор полей в классе С# не выполняется при десериализации
У меня есть класс, который определяет защищенное поле. Защищенное поле имеет инициализатор поля.
Когда я десериализую конкретный класс, инициализатор поля не запускается. Зачем? Каков наилучший шаблон для решения проблемы? Если я переношу инициализацию в конструктор, конструктор также не вызывается.
[DataContract]
public class MyConcrete
{
// FIELD INITIALIZER DOES NOT RUN WHEN COMMENTED IN:
protected readonly Dictionary<int, string> myDict;// = new Dictionary<int, string>();
public MyConcrete()
{
myDict = new Dictionary<int, string>();
}
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<int, string> myDict = new Dictionary<int, string>();
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>();
MyConcrete rehydrated = SerializationHelper.DeserializeFromString<MyConcrete>(data);
}
}
ПОДДЕРЖИВАЮЩИЕ МЕТОДЫ
static public string SerializeToString<T>(this T obj)
{
return SerializationHelper.SerializeToString<T>(obj);
}
static public string SerializeToString<T>(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<T>(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;
}
}
Ответы
Ответ 1
При десериализации не используются ни конструкторы, ни инициализаторы полей, а вместо этого используется "пустой" неинициализированный объект.
Чтобы решить эту проблему, вы можете использовать OnDeserializing
или OnDerserialized
, чтобы десериализатор вызывал функцию со следующей сигнатурой:
void OnDeserializing(System.Runtime.Serialization.StreamingContext c);
В этой функции вы можете инициализировать все, что было пропущено в процессе десериализации.
В терминах соглашения я склоняюсь к тому, что мой конструктор вызывает метод OnCreated()
, а также метод десериализации - это одно и то же. Затем вы можете обрабатывать всю инициализацию поля и быть уверенным, что он был запущен до десериализации.
[DataContract]
public abstract class MyAbstract
{
protected readonly Dictionary<int, string> myDict;
protected MyAbstract()
{
OnCreated();
}
private void OnCreated()
{
myDict = new Dictionary<int, string>();
}
[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; }
}
}
Ответ 2
Другим подходом является доступ к вашему полю через защищенное (в вашем примере) свойство и инициализация поля с помощью null-coalescing (??
)
protected Dictionary<int, string> myDict = new Dictionary<int, string>();
protected Dictionary<int, string> MyDict
{
get
{
return myDict ?? (myDict = new Dictionary<int, string>());
}
}
Недостатки в том, что вы теряете преимущества readonly
, и вам нужно убедиться, что вы получаете доступ только к значению через свойство.