[SerializeReference] and PropertyDrawers not serializing?

Im trying to write a property drawer where one can select a serialized reference, but I can’t get the selection to stick. I’ve whittled it down to the smallest example, and I can’t figure out where Im going wrong…

The object I wish to serialize:

[Serializable]
public class ReferenceType
{
    public ReferenceType() { this.name = "Nothing"; }
    public ReferenceType(string name) { this.name = name; }

    public readonly string name;
}

The Mono that is referencing it:

public class ReferenceTester : MonoBehaviour
{
    [SerializeReference]
    ReferenceType refType;

    void Update()
    {
        Debug.Log("Type is " + refType.name + ". Should be Something");
    }
}

The Property Drawer:

[CustomPropertyDrawer(typeof(ReferenceType))]
public class ReferenceTypeDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        property.serializedObject.Update();

        if (property.managedReferenceValue == null
        || ((ReferenceType)property.managedReferenceValue).name != "Something")
            property.managedReferenceValue = new ReferenceType("Something");

        property.serializedObject.ApplyModifiedPropertiesWithoutUndo();
    }
}

When I click play, the editor displays:

HOWEVER, while playing, if select the game object to inspect it, the log changes to:

It will stay the proper value until I run the game again, at which point the problem repeats.

Anyone have any advice?

Why do you use SerializeReference here? ReferenceType is just a normal class, so it’ll work just fine with SerializeField. Unless you stripped out the part where you are subclassing an abstract base class.

I guess the issue here is with readonly. As Unity deserializes the object, it will run the parameterless constructor. Then it will deserialize the fields. But … oops, that field is readonly! Thus it cannot be deserialized and the value remains “Nothing”. Until your property drawer replaces refType with a new object that gets initialized with “Something”.

Try removing the readonly modifier.

1 Like

Yup, the main project has an abstract base class. The idea is that, depending on the type set in the component, the drawer will display a different set of options pulled from it and its children in the inheritance hierarchy. An enum with inheritance basically.

That did it! Excellent explanation too.

Keep in mind that you can use a read-only property instead. So you can have a serialized private backing field but only provide a getter in the property. So from outside the class you can’t accidentally modify the name

[SerializeField]
private string m_Name;
public string name => m_Name;

It’s also possible with an auto property:

[field:SerializeField]
public string name {get; private set;}