Search code examples
c#serializationc#-4.0binary-serialization

Customize a struct so that it serializes as a Int32


I save data using binary serialization. Now I have changed a field in the program from Int32 to a struct. But I still want to save the field as Int32 to be backward compatible. How do I do that?

background information My program is ported from Delphi and uses a lot of arrays. The problem is that the lists in Delphi start counting from 1, vilkort has made it difficult to keep track of the indexes when I am now developing in C#.So I use a struct to make it easier to deal with the 1-based index of the 0-based arrays until I have corrected the whole program.

The code

    public void Save()
    {
            using (var writer = File.Open(Path, FileMode.Create))
            {
                var serializer = new BinaryFormatter();
                serializer.Serialize(writer, _data);
            }
    }

    private void Load()
    {
        using (var reader = File.Open(Path, FileMode.Open))
        {
                var deserializer = new BinaryFormatter();
                _data = (Foo) deserializer.Deserialize(reader);
        }
    }

public struct Indexer
{
    //0-baserat index
    private int _index;

    public Indexer(int index)
    {
        _index = index;
    }

    static public implicit operator Indexer(int index)
    {
        return new Indexer(index);
    }

    static public implicit operator int(Indexer indexer)
    {
        return indexer._index;
    }

    public override string ToString()
    {
        return (_index + 1).ToString();
    }

    ...
  }

Solution

  • You can add a legacy field of Int32 type, and also have the struct field. Make a method with the [OnDeserialized] attribute (and the correct arguments) and initialize the struct field from the int field under some circumstances, for example that the int field is nonzero and the struct has default value after deserialization, indicating that the data is from the older format.

    private int intValue;                // Legacy field
    private SomeStruct structValue;      // New field
    
    [OnDeserialized]
    private void OnDeserialized(StreamingContext context)
    {
      if (intValue != 0)
      {
          // Old format, initialize struct with int value.
          structValue = new SomeStruct(intValue);
      }
    }
    

    This is not exactly elegant and there are other ways, but if you don't mind keeping two fields this is one of the simpler ways. Note that the above code assumes that zero indicates a "missing" value for the old int field.