反序列化时,C#类中的字段初始化程序不运行

Eri*_* J. 28 c# serialization

我有一个定义受保护字段的类.受保护的字段具有字段初始值设定项.

当我反序列化具体类时,不会运行字段初始值设定项.为什么?解决问题的最佳模式是什么?如果我将初始化移动到构造函数中,也不会调用构造函数.

[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
    }
}
Run Code Online (Sandbox Code Playgroud)

原始类层次结构

[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);
    }
}
Run Code Online (Sandbox Code Playgroud)

支持方法

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Red*_*dog 45

在反序列化时,既不调用构造函数也不调用字段初始值设定项,而是使用"空白"未初始化对象.

要解决它,您可以使用OnDeserializingOnDerserialized属性让反序列化器调用具有以下签名的函数:

void OnDeserializing(System.Runtime.Serialization.StreamingContext c);
Run Code Online (Sandbox Code Playgroud)

在该函数中,您可以初始化反序列化过程中遗漏的任何内容.

在约定方面,我倾向于让我的构造函数调用一个方法OnCreated(),然后还有deserializating方法调用相同的东西.然后,您可以在那里处理所有字段初始化,并确保在反序列化之前触发它.

[DataContract]
public abstract class MyAbstract
{
    protected 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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我不能从OnDeserializing()方法中分配一个只读字段...这个序列化程序的设计选择破坏了只读的能力......但似乎是最好的/唯一的方法. (5认同)
  • @TimSylvester:几乎所有东西都可以通过反射:-)遗憾的是,语言设计者会强迫对象设计师使用反射来确保对象始终被正确初始化. (3认同)

Ric*_*ett 9

另一种方法是通过受保护的(在您的示例中)属性访问您的字段,并使用null-coalescing(??)运算符初始化该字段

protected Dictionary<int, string> myDict = new Dictionary<int, string>(); 

protected Dictionary<int, string> MyDict
{
    get
    {
        return myDict ?? (myDict = new Dictionary<int, string>());
    }
}
Run Code Online (Sandbox Code Playgroud)

缺点是您失去了好处readonly,并且需要确保您只通过该属性访问该值.