Automatic Properties and the BinaryFormatter
When I first encountered the C# 3.0 feature known as 'automatic properties' I had two, almost simultaneous, thoughts: "Oh great!" and "Oh no!" Here is an automatic property:
public int Bar { get; set; }
This property does not reference a field, but I assure you it compiles just fine. This is very convenient for when you want to quickly add a property to a type, but do not want to bother typing all of this:
private int _bar;
public int Bar
{
get { return _bar; }
set { _bar = value; }
}
I was concerned about this great new language feature. More than a few times in the past I have been burnt by the BinaryFormatter. I would serialize an instance of a class, save it to disk, upgrade the DLL containing the class of the serialized object, and encounter an exception when deserializing that object during a subsequent run of the app. If the type that has instances serialized was given a new field, had a field removed, or had a field renamed, the BinaryFormatter would throw an exception. It demanded that the object being deserialized matched the type as which it was being instantiated.
Here was my line of thinking...An automatic property uses a compiler-generated field as its backing store. The name of that field is, practically speaking, random nonsense. If I later decide to turn an automatic property into a normal property, with a corresponding field, I will have to change the name of that field. The auto-generated field will disappear and my field will instead be used. In that situation, wouldn't the BinaryFormatter throw an exception when deserializing an instance of the class, if it was serialized when the class still had an automatic property (with a compiler-generated field name)? If so, that would be a subtle and nasty bug to track down!
I performed a test and discovered that this issue leaves us, the software debuggers of the world, in a bad situation. It seems that BinaryFormatter has mellowed with age. It does not throw exceptions any more when a field in the class is renamed or removed between object serialization and deserialization. Instead, it just ignores the situation and moves on. What this means is that, upon deserialization, the property that used to be "automatic" will no longer have its previous value. Since its corresponding backing field was renamed, it is no longer set by BinaryFormatter. To be honest, I would prefer that an exception was thrown in this situation just so that I knew right away that there was a problem. This is something to keep in mind when using and altering automatic properties in serializable types.
Here is the console app I created to test this:
#define AUTO
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace AutomaticPropertyTest
{
class Program
{
static void Main(string[] args)
{
Foo foo = null;
#if AUTO
foo = new Foo();
foo.Bar = 42;
Console.WriteLine("Writing Foo with automatic property to file...");
using (FileStream strm = new FileStream("asdf.dat", FileMode.Create))
new BinaryFormatter().Serialize(strm, foo);
#else
Console.WriteLine("Reading Foo with explicit backing field from file...");
using (FileStream strm = new FileStream("asdf.dat", FileMode.Open))
foo = new BinaryFormatter().Deserialize(strm) as Foo;
#endif
Console.WriteLine("foo.Bar = " + foo.Bar);
Console.ReadKey();
}
}
[Serializable]
class Foo
{
#if AUTO
public int Bar { get; set; }
#else
private int _bar;
public int Bar
{
get { return _bar; }
set
{
if (value < 0)
throw new ArgumentOutOfRangeException("value");
_bar = value;
}
}
#endif
}
}
First run the app with the AUTO token defined. That will save an instance of the Foo class to disk, with an automatic property called Bar. Then comment out the AUTO token definition and run again. This replaces the Bar property with a "normal" property and field, and deserializes the Foo object from disk. The result is that Bar, which was previously set to 42, will return 0. In a real debugging scenario, this could be a nightmare to track down, especially if you were not aware of this subtle side-effect of altering automatic properties.
I think a good rule of thumb is: do not use automatic properties in serializable types.