Search code examples
c#serializationbinaryformatteriserializable

Serialization when extending a class whose GetObjectData method is not marked virtual


I'm trying to extend a framework. One of the classes I am extending is serialized. The base class' GetObjectData() method is not marked virtual so I can't override it.

Now if object gets serialized when it's referenced as a base class it's not polymorphic so only the base class' GetObjectData is called.

Is there any way around this without modifying the base class' GetObjectData to mark it as virtual?

[Edit] I extended the class and added an attribute that I want to serialize. Simple example of the problem below

[Serializable]
public class ParentClass : ISerializable 
{

    public float m_parent = 1.73f;

    public ParentClass() 
    { }

    public ParentClass(SerializationInfo info, StreamingContext context)
    {
        Debug.Log("Loading parent");
        m_parent = (float)info.GetValue("m_parent", typeof(float));
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        Debug.Log("Saving parent");
        info.AddValue("m_parent", m_parent, typeof(float));
    }
}


[Serializable]
public class ChildClass : ParentClass
{
    public int m_child = 73;

    public ChildClass()
    { }

    public ChildClass(SerializationInfo info, StreamingContext context) : base(info, context)
    {
        Debug.Log("Loading child");
        m_child = (int)info.GetValue("m_child", typeof(int));
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        Debug.Log("Saving child");
        info.AddValue("m_child", m_child, typeof(int));
        base.GetObjectData(info, context);
    }
}

void Save()
{
    Debug.Log("Starting save");
    ParentClass aParent = new ChildClass() as ParentClass;
    using (Stream stream = File.Open(fileName, FileMode.Create))
    {
        BinaryFormatter bFormatter = new BinaryFormatter();
        bFormatter.Serialize(stream, aParent);
    }
    Debug.Log("Save complete");
}

void Load()
{
    Debug.Log("Starting load");
    ChildClass aChild;
    using (Stream stream = File.Open(fileName, FileMode.Open))
    {
        BinaryFormatter bFormatter = new BinaryFormatter();
        aChild = bFormatter.Deserialize(stream) as ChildClass;
    }
    Debug.Log("Load complete" + aChild.m_child);
}

Doing a save/load yields the following error:

SerializationException: No element named m_child could be found.


Solution

  • You need to do two things:

    1. Mark ChildClass explicitly as being ISerializable.

    2. Declare a GetObjectData with the new modifier in the child class as Will suggests,

      or

      Implement the interface explicitly in the child class.

    For instance:

    [Serializable]
    public class ChildClass : ParentClass, ISerializable
    {
        public int m_child = 73;
    
        public ChildClass()
        { }
    
        public ChildClass(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
            Debug.WriteLine("Loading child");
            m_child = (int)info.GetValue("m_child", typeof(int));
        }
    
        public new void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            Debug.WriteLine("Saving child");
            info.AddValue("m_child", m_child, typeof(int));
            base.GetObjectData(info, context);
        }
    }
    

    Since BinaryFormatter.Serialize(Stream, Object) is non-generic, the most derived interface implementation will be discovered and used.

    For details of why this works, see the c# language specification 17.6.7 Interface re-implementation:

    A class that inherits an interface implementation is permitted to re-implement the interface by including it in the base class list.