【问题标题】:Field Initializer in C# Class not Run when Deserializing反序列化时 C# 类中的字段初始化程序不运行
【发布时间】:2012-03-14 06:04:57
【问题描述】:

我有一个定义受保护字段的类。受保护的字段有一个字段初始化器。

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

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

【问题讨论】:

  • 尝试在初始化字典的抽象中添加受保护的构造函数?在 MyConcrete 中提供一个链接到 : base() 的公共构造函数。
  • 我将它扁平化为一个类并添加了一个构造函数。构造函数没有被调用。发现此相关帖子声称未调用字段初始值设定项和构造函数...stackoverflow.com/questions/5021973/…

标签: c# serialization


【解决方案1】:

在反序列化neither the constructors nor the field initializers are called 时,使用“空白”未初始化对象。

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

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

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

按照惯例,我倾向于让我的构造函数调用一个方法OnCreated(),然后也让反序列化方法调用相同的东西。然后,您可以在其中处理所有字段初始化,并确保在反序列化之前触发它。

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

【讨论】:

  • 我无法从 OnDeserializing() 方法中分配只读字段...序列化程序的这种设计选择破坏了使用只读的能力...不过,似乎是最好/唯一的方法。
  • 哦,是的...关于只读的,您是对的。糟糕,错过了!
  • @Reddog +1 感谢这个解决方案,我可以在没有只读的情况下生活
  • 通过反射仍然可以读取只读字段:stackoverflow.com/a/12398681/135138
  • @TimSylvester:几乎所有事情都可以通过反射实现 :-) 遗憾的是语言设计者会强迫对象设计者使用反射来确保对象始终正确初始化。
【解决方案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 的好处,并且需要确保您只能通过该属性访问该值。

【讨论】:

  • +1,提供类似于 readonly 的语义,只要所有从事该类工作的开发人员都理解访问MyDict 而不是myDict 的约定。某处会导致错误发生,但适合某些情况的选项。
  • 这会给你上课带来懒惰——对于只读属性来说很酷。
  • 为什么不完全删除字段初始值设定项?除非你需要它,否则没有理由创建字典。
猜你喜欢
  • 1970-01-01
  • 2019-04-28
  • 1970-01-01
  • 1970-01-01
  • 2018-10-21
  • 1970-01-01
  • 2015-04-18
  • 2022-12-11
相关资源
最近更新 更多