Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Consider the following naïve data contract:
[DataContract]
public class Data
{
private int[] array;
public Data()
{
this.array = new int[13];
}
public int Length { get { return this.array.Length; } }
}
It looks ok, right? Let’s use it then:
DataContractSerializer serializer = new DataContractSerializer(typeof(Data));
Data data = new Data();
using (MemoryStream stream = new MemoryStream())
{
serializer.WriteObject(stream, data);
stream.Position = 0; // Rewind
data = (Data)serializer.ReadObject(stream);
Console.WriteLine(data.Length); // throws a NullReferenceException
}
Why was this.array not initialized? Well, I gave the answer away in the title; the constructor was never called. It turns out that DataContractSerializer (and BinaryFormatter, by the way), unlike XmlSerializer, creates an uninitialized (without calling any constructors) instance of the type it’s de-serializing. Then data members are de-serialized.
In the example above, a very simple change will fix the issue. If you’re running .Net 3.5 SP1 or later (and you should be), the DataContractSerializer can serialize types without the [DataContract] attribute, i.e. Plain Old CLR Object (POCO) types, as long as the type is public and has a parameter-less constructor (regardless of its visibility) – just like XmlSerializer. And yes, you guessed it, in this case, the constructor will be called.
What if you don’t want to define a parameter-less constructor, or if this isn’t a viable option? You should use one of the serialization callbacks listed in the following article: https://msdn.microsoft.com/en-us/library/ms733734(v=vs.110).aspx
Here’s an example:
[DataContract]
public class Data
{
private int[] array;
public Data()
{
Initialize();
}
private void Initialize()
{
this.array = new int[13];
}
[OnDeserializing]
private void SetValuesOnDeserializing(StreamingContext context)
{
Initialize();
}
public int Length { get { return this.array.Length; } }
}