Generic type serialization + SerializedObject

I’m playing with 2020.1 beta, the “generics serializaton” is a nice touch (finally!).
Is it planned to be supported together with the SerializedObject and SerializedProperty APIs ?

Right now, in my simple example, i’m trying to iterate over a component that has a generic type as a serialized field. The SerializedProperty returns ‘CachedValue`1’ as the type and null as the objectReferenceValue.

Should this work? or am i doing something wrong?

This is my component BTW:

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

/// <summary>
/// Represents a value that is cached in PlayerPrefs.
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
public class CachedValue<T>
{
    [SerializeField]
    private T myValue;
   
    // TODO: more code here - flush to PlayerPrefs when dirty.
}

public class LiorScript : MonoBehaviour
{
    public CachedValue<int> intCache;
    public CachedValue<string> stringCache;
    public CachedValue<Vector3> vector3Cache;
}

SerializedProperty.objectReferenceValue is to refer to an object that inherits from UnityEngine.Object, so that should be null.

Man it’s a bummer that it’s not returning the generic type arguments in the name. If not, you can use a script to get the System.Object value by walking down the property path from the SerializedObject.target. Once you get that, it should be as simple as obj.GetType().

It’s pretty useful to have a “GetObjectValue” extension method for Serialized Properties. Here’s a snippet of the one I use:

private static readonly Regex ArrayPathRegex = new Regex(
    @"(?<arrayName>\w+)\[(?<index>\d+)\]",
    RegexOptions.Compiled);

public static object GetValue(this SerializedProperty property,
    Func<IEnumerable<string>, IEnumerable<string>> modifier = null)
{
    IEnumerable<string> path = property.propertyPath.Replace(".Array.data[", "[").Split('.');
    if (modifier != null)
    {
        path = modifier(path);
    }
    var target = (object)property.serializedObject.targetObject;
    return GetValueRecur(target, path);
}

private static object GetValueRecur(object target, IEnumerable<string> path)
{
    if (target == null) throw new ArgumentNullException(nameof(target));

    var head = path.FirstOrDefault();
    if (head == null)
    {
         return target;
    }

    var arrayMatch = ArrayPathRegex.Match(head);
    if (arrayMatch != null)
    {
        head = arrayMatch.Groups["arrayName"].Value;
        var index = int.Parse(arrayMatch.Groups["index"].Value);
        var field = target.GetType().GetField(head, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
        var array = field.GetValue(target) as IEnumerable;
        target = array.ElementAtOrDefault(index);
    }
    else
    {
        target = target.GetType().GetField(head).GetValue(target);
    }
    return GetValueRecur(target, path.Skip(1));
}

The Skip and ElementAt are IEnumerable extensions. You can browse them here: Jason Elie Bou Kheir / Unity Editor Extensions · GitLab. You can make a simpler version that doesn’t account for arrays.