Problem with [SerializeReference] attribute and polymorphism

Hi,

I’m having some trouble with the [SerializeReference] attribute. It seems that my objects are not stored correctly, whenever I reload a scene, I get lots of null pointer exceptions.

I have the following script attached to a couple of GameObjects in the scene (toy example obviously):

using System.Collections;
using UnityEngine;
using Classes;

[System.Serializable]
public class SceneObject : MonoBehaviour
{
    [SerializeField] public ClassEnum classType;
    [SerializeReference] public AbstractClass instance;

    void Start()
    {
        Debug.Log(instance.doSomething());
    }
}

There’s a custom editor for it that has a drop-down list for the 3 different types (A, B and C). Once chosen, an object of the corresponding type is instantiated.

using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using Classes;

[CustomEditor(typeof(SceneObject))]
public class SceneObjectEditor : Editor
{
    public override void OnInspectorGUI()
    {
        SceneObject sceneObj = (SceneObject)target;

        ClassEnum newType = (ClassEnum)
                EditorGUILayout.EnumPopup(
                    "Class Type",
                    sceneObj.classType);

        if (newType != sceneObj.classType)
        {
            switch (newType)
            {
                case ClassEnum.A:
                    sceneObj.instance = new ClassA();
                    break;
                case ClassEnum.B:
                    sceneObj.instance = new ClassB();
                    break;
                case ClassEnum.C:
                    sceneObj.instance = new ClassC();
                    break;
            }

            sceneObj.classType = newType;
        }

        if (!Application.isPlaying && GUI.changed)
        {
            EditorUtility.SetDirty(target);
            EditorSceneManager.MarkSceneDirty(sceneObj.gameObject.scene);
        }
    }
}

And here are the classes I’m trying to serialize:

namespace Classes
{
    [System.Serializable]
    public abstract class AbstractClass
    {
        public abstract string doSomething();
    }

    [System.Serializable]
    public class ClassA : AbstractClass
    {
        public override string doSomething()
        {
            return "I am A";
        }
    }

    [System.Serializable]
    public class ClassB : AbstractClass
    {
        public override string doSomething()
        {
            return "I am B";
        }
    }

    [System.Serializable]
    public class ClassC : AbstractClass
    {
        public override string doSomething()
        {
            return "I am C";
        }
    }

    [System.Serializable]
    public enum ClassEnum
    {
        A,
        B,
        C
    }
}

If I change the value with my drop down list and then hit play, I get all the correct messages. As soon as I reload the scene, I will get null pointers, until I use the inspector to select a different type again. What am I doing wrong?

The confusing part is, I have a different script where I serialize a list of polymorphic types and it works just fine. I cannot for the life of me figure out what’s different. Did something about SerializeReference change in the 0b6 update?

  1. see if your serialized scene file has the reference saved

  2. try to use SerializedObject for the custom inspector

  3. there are some issues if the base class is not an interface, maybe is that. send a bug report to Unity

Fixed it!

Apparently Unity seems to decide that objects without fields are not worth deserializing, so it just tosses them away. If I change ClassA as follows:

[System.Serializable]
    public class ClassA : AbstractClass
    {
        [SerializeField] public float hack;

        public override string doSomething()
        {
            return "I am A";
        }
    }

Then it will carry over on scene reload just fine. This is also why my other example worked, those classes all have fields.

I’ll probably post a bug report about this later.

1 Like

Please do!

That would be much appreciated. Please post the issue ID here as well.

Thanks for coming back with the fix. Did you repor the bug? Since I am still getting the same problem in 2019.3.10

Seems like it is still not fixed so here is a nicer workaround:
[HideInInspector, SerializeField] bool hack;

Is this now fixed? In what version?