Generics serialization

  • Scripting: The serializer can now serialize fields of generic types (e.g. MyClass someField) directly; it is no longer necessary to derive a concrete subclass from a generic type in order to serialize it.

I’d argue this beauty needs its own entry in the 2020.1 features list! :slight_smile:

16 Likes

That’s neat!

I’d love to know how this interacts with property drawers. How would we go about defining a custom property drawer for a generic class?

4 Likes
[CustomPropertyDrawer(typeof(Container<>))]
public class ContainerPropertyDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.PropertyField(position, property.FindPropertyRelative("Value"), new GUIContent(fieldInfo.FieldType.GetGenericArguments()[0].FullName));
    }
}

[CustomPropertyDrawer(typeof(Container<string>))]
public class StringContainerPropertyDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.PropertyField(position, property.FindPropertyRelative("Value"), new GUIContent("String!"));
    }
}

This works. You can get the generic argument type with fieldInfo.FieldType.GetGenericArguments(). Plus it looks like it picks the more specific attribute if it exists.

11 Likes

Cool, thanks. That’ll be really, really useful. We have a SerializableDictionary implementation, and each of them requires both a concrete subclass and a concrete drawer subclass, so this will save us a ton of code.

3 Likes

Best feature after nested prefabs

6 Likes

What about generic SerializeReference ?

using System;
using UnityEngine;

[Serializable]
public class A
{
    public int a;
}

[Serializable]
public class B : A
{
    public int b;
}

[Serializable]
public class C : A
{
    public int c;
}

[Serializable]
public class D<T> : A
{
    public T d;
}

public class TestSerializeReference : MonoBehaviour
{
    [SerializeReference]
    public A a = null;

    public D<int> d = new D<int>();

    public bool changeType = false;

    private void OnValidate()
    {
        if (changeType)
        {
            if (a == null || a is D<int>)
            {
                a = new A();
            }
            else if (a is C)
            {
                a = new D<int>();
            }
            else if (a is B)
            {
                a = new C();
            }
            else
            {
                a = new B();
            }

            changeType = false;
        }
    }
}

I took a quick look at it recently and only got an editor crash when executing “a = new D();”. Hope it’s juste an alpha bug and that it will be supported in the future.

This looks nice, tested a few cases. This will probably enable Unity to also mark the UnityEvent classes as serializable and make them non abstract; so that we could serialize UnityEvent directly without creating a subclass first.

5 Likes

Yep, just found that too and reported it with a tiny repro. It crashes if any generic class is directly serialized with SerializeReference with compute_class_bitmap: Invalid type 13 for field Container`1[T]:Value

@LeonhardP Case 1185214

2 Likes

All editor crashes are bugs that should be reported. It might not be that the feature is supposed to be supported, but nothing should crash the editor.

6 Likes

Are list of generic classes supposed to show up in the inspector?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestSerialization : MonoBehaviour
{
    [System.Serializable]
    public class D<T>
    {
        public T d;
        public T d2;
    }

    public List<D<int>> myclass = new List<D<int>>();// doesn't show up in the inspector'
    public D<int> myclass2;
}

There’s an open bug about that right now. The main issue is fixed but we’re resolving one secondary issue about how it interacts with [SerializeReference].

2 Likes

It’s probably the following issue, in case you want to keep track:
https://forum.unity.com/threads/case-1188478-list-of-generic-type-not-serialized.754442/

1 Like

Is there any plan to support generics for scriptable objects too?
Knowing enough about how they work and comments about UnityEngine.Object not being supported together with SerializeReference this is probably a no.

In the below example, genericMonoA showed in inspector as an Object field(as expect),
but genericMonoB was treated as a pure class with foldout in inspector, and will call new() on game start (prohibited for MonoBehaviour)
Is this some kind of bug?

using UnityEngine;

[System.Serializable]
public class GenericMono<T> : MonoBehaviour
{
    [SerializeField]
    T t;
}

public class GenericMonoFloat : GenericMono<float> { }

public class GenericBehaviour : MonoBehaviour
{
    [SerializeField]
    GenericMonoFloat genericMonoA;

    [SerializeField]
    GenericMono<float> genericMonoB;
}

Is there a way to serialise it manually and get the result? Similar to how JSONUtility works - it has the same constraints as the current serialisation used by Unity. Is there an equivalent for this new serialisation?

Yes, I think so - please file a bug report via the Editor so our QA team can process it properly.

I’m not sure what you mean. This new behaviour is just an extension to the existing serialiser - it’s not a whole new serialisation system.

This feature doesn’t seem to work with arrays/lists of types that have a generic parameter. For example…

[CreateAssetMenu(menuName = "Tables/Weapon")]
public class WeaponTable : Table<Weapon>
{
}

public abstract class Table<TValue> : ScriptableObject
{
    public Row<TValue>[] rows;
}

[Serializable]
public struct Row<TValue>
{
    public TValue value;

    public int rating;
}

It works if table has just one row but not an array of them.

bug report sent: 1199278
Thank you for the reply! I always appreciate Unity’s speedy supports

2 Likes

Yes we should be removing the abstract from UnityEvent in the future :slight_smile:

9 Likes