Recently in our project I had a weird exception popping up in C#. We found the problem and fixed it… but the problem was something we didn’t expect ! So I’ll post my findings here so other people can benefit from it.
I must say, I don’t know if this behaviour is anywhere described in MSDN, so could be I missed the information.
Consider this class Foo with only 1 member O1 that is Serializable.
[Serializable]
public class Foo
{
private object _O1;
public object O1
{
get { return _O1; }
set { _O1 = value; }
}
public Foo()
{
O1 = new object();
}
}
Now we are going to save an instance of this Foo class to file through Serialization ! ( here done through unittesting with NUnit )
[Test]
public void SerializeTest()
{
Foo foo = new Foo();
Assert.IsNotNull(foo.O1);
BinaryFormatter formatter = new BinaryFormatter();
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
FileStream stream = new FileStream(@"c:\temp\foo.dat", FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, foo);
}
Now we have a binary representation of Foo on file, called foo.dat !
After this, we change the Foo class and add a second member called O2.
[Serializable]
public class Foo
{
private object _O2;
public object O2
{
get { return _O2; }
set { _O2 = value; }
}
private object _O1;
public object O1
{
get { return _O1; }
set { _O1 = value; }
}
public Foo()
{
O1 = new object();
O2 = new object();
}
}
And now for the big problem… we Deserialize the foo.dat file with this new Foo class code.
[Test]
public void DeserializeTest()
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(@"c:\temp\foo.dat", FileMode.Open, FileAccess.Read, FileShare.Read);
Foo foo = (Foo)formatter.Deserialize(stream);
Assert.IsNotNull(foo.O2);
}
And what do you notice ? The test wil FAIL !! Yes… what we didn’t expect was the fact that .Net somehow can initialize the Foo object without going through it’s default constructor !!
Resulting in a Foo object with a O2 member that equals NULL instead of new Object() !
This would mean that if you need to deserialize newer class versions of existing binary objects, you should use the interface ISerializable and use protected Foo(SerializationInfo info, StreamingContext context) to be sure all members ( and new members ) are serialized and initiated ?
Can anyone shed some light on this ?
[Update] Jelle has given me the correct link to the MSDN documentation that gives the answer to this phenomenon !! Thanks Jelle ! http://msdn2.microsoft.com/en-us/library/4abbf6k0(VS.71).aspx
[tags]C#, .Net, Visual Studio[/tags]