Инициализатор поля в классе C # не запускается при десериализации - PullRequest
25 голосов
/ 23 февраля 2012

У меня есть класс, который определяет защищенное поле.Защищенное поле имеет инициализатор поля.

При десериализации конкретного класса инициализатор поля не запускается.Зачем?Каков наилучший шаблон для решения проблемы?Если я перенесу инициализацию в конструктор, конструктор также не будет вызван.

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

Ответы [ 2 ]

42 голосов
/ 23 февраля 2012

При десериализации ни конструкторы, ни инициализаторы полей не называются , и вместо них используется "пустой" неинициализированный объект.

Для его разрешения вы можете использовать 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; }
    }
}
9 голосов
/ 05 сентября 2012

Другой подход состоит в том, чтобы получить доступ к вашему полю через защищенное (в вашем примере) свойство и инициализировать поле с помощью оператора 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, и вам необходимо убедиться, что вы получаете доступ только к значению через свойство.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...