3

I have a class like this:

public class Foo
{
    public IBar {get;set;}
    //tons of other properties
}

public interface IBar
{
    //whatever
}

The class is used for binary serialization (standard use of BinaryFormatter). An implementation of IBar is marked with [Serializable] so everything works.

Now I want not to serialize Bar and preserve backwards compatibility (it was not referenced in the code anyway). NonSerialized attribute seems to be enough. However it can be applied only to fields, not to automatic properties. So I tried this:

public class Foo
{
    private IBar _bar;
    [NonSerializable]
    public IBar Bar 
    {
        get { return _bar; }
        set { _bar = value; }
    }
}

Suprisingly it works well - I can both deserialize old Foos and the new ones.

My question is: how can it possibly work if these are the fields that are serialized and the automatic property's backing field is likely to have some non-C# characters in its name?

In other words:

Old Foo's IBar field name (my guess): k__BackingField

New Foo's IBar field name: _bar

Obviously they don't match, so how BinaryFormatter overcomes this?

dzendras
  • 4,721
  • 1
  • 25
  • 20
  • When you say the property was never referenced in code doesn't that mean it was always `null`? If so the answer to why it can deserialize across the binary incompatibility is that it's actually never had to. – JRoughan Feb 28 '13 at 11:06
  • I was not precise enough. The property was assigned a value in constructor. It was never referenced elsewhere. Frankly, it should be deleted from Foo. – dzendras Feb 28 '13 at 12:08
  • Your "old" code is syntactically erroneous -- please specify the property name as it almost certainly impacts the behavior. – Ben Voigt May 31 '17 at 15:23

1 Answers1

1

I think there is something strange in your example. BinaryFormatter shouldn't be able to handle this (as far as I know, unless this is changed in 4.5 which I doubt), which is why it is quite dangerous to use if backwards compatibility is necessary. Are you sure the value is serialized from the old version and deserialized to the new version? Can you verify that the deserialized data matches, and aren't null?

For a complete example of a program that verifies that it does not work, see here. http://www.infragistics.com/community/blogs/josh_smith/archive/2008/02/05/automatic-properties-and-the-binaryformatter.aspx

You will not see any exceptions, but the old value from the field named xyz__backingfield will be lost, and replaced in the new class by a default value.

If you want to be backwards compatible, avoid using automatic properties, or you will be in a world of trouble very soon. In fact it doesn't really matter, since the BinaryFormatter in default (automatic) mode is only really useful if you want to serialize objects and deserialize them again in the same application, for example for copy & paste or a similar operation. In that case you have no versioning issues since it will be the same code doing both the serialization and deserialization.

To make serialization backwards compatible without losing your mind, make sure you have full control of the schema. Good examples of serializers where you have a decent chance of staying out of trouble are DataContractSerializer, Json.NET or Protocol buffers (for example protobuf-net).

As a last possibility you can implement ISerializable and use the dictionary storage of BinaryFormatter, but then you have all the drawbacks of hand-rolling your serialization anyway.

On a sidenote if you want to apply attributes to a backing field try [field:AttriuteType] which is useful to mark backing fields of events as non serialized for example .

Anders Forsgren
  • 10,827
  • 4
  • 40
  • 77
  • Thank you for your response. Unfortunately it's too late to switch to any other serializer. I have to make a workaround using BinaryFormatter and when there's time (in the future) get rid of BinarySerialization completely. – dzendras Feb 28 '13 at 13:10