Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Field Initializer in C# Class not Run when Deserializing

I have a class that defines a protected field. The protected field has a field initializer.

When I deserialize the concrete class, the field initializer is not run. Why? What is the best pattern to solve the problem? If I move the initialization into a constructor, the constructor is also not invoked.

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

ORIGINAL CLASS HIERARCHY

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

SUPPORTING METHODS

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;     } } 
like image 553
Eric J. Avatar asked Feb 23 '12 19:02

Eric J.


People also ask

What is a field initializer?

A field initializer allows you to initialize a field inline, instead of inside of a constructor. For example, instead of doing: class MyClass() { int a; int b; public MyClass() { a = 5; b = 3; } }

What is designated initializer in C?

A designated initializer, or designator, points out a particular element to be initialized. A designator list is a comma-separated list of one or more designators. A designator list followed by an equal sign constitutes a designation.

Do fields have to be initialized?

A required field must be initialized by the constructor, or by an object initializers when an object is created. You add the System.

Can you initialize variables in a struct C?

Structure members cannot be initialized with declaration.


1 Answers

On deserialization neither the constructors nor the field initializers are called and a "blank" un-initialized object is used instead.

To resolve it you can make use of the OnDeserializing or OnDerserialized attributes to have the deserializer call a function with the following signature:

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

In that function is where you can initialize whatever was missed within the deserialization process.

In terms of convention, I tend to have my constructor call a method OnCreated() and then also have deserializating method call the same thing. You can then handle all of the field initialization in there and be sure it's fired before deserialization.

[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; }     } } 
like image 160
Reddog Avatar answered Sep 22 '22 22:09

Reddog