AnnulusGames / LucidEditor

Powerful Editor Extensions for Unity
MIT License
127 stars 13 forks source link

SerializedPropertyExtensionsで ValueType の配列と List は IEnumerable<object> にキャストできない #5

Closed Nattuhan closed 1 year ago

Nattuhan commented 1 year ago

発生した問題

[Serializable]struct を配列としてインスペクター上でシリアライズしたとき、SerializedPropertyExtensions.cs 207行目でエラーが出た https://github.com/AnnulusGames/LucidEditor/blob/790284da99c9201a5b308a4d9099a6e7634aa7bd/Assets/LucidEditor/Editor/Extensions/SerializedPropertyExtensions.cs#L207

解決の打診

GetNestedObject関数以下を以下のように置き換え、GetElementOrDefaultという関数を追加しました。 コメントを残しておりますのでこちら参考いただけますと幸いです 🙇

private static T GetNestedObject<T>(string path, object obj, bool includeAllBases = false)
{
    string[] parts = path.Split('.');
    for (int i = 0; i < parts.Length; i++)
    {
        string part = parts[i];

        if (part == "Array")
        {
            var regex = new Regex(@"[^0-9]");
            var countText = regex.Replace(parts[i + 1], "");
            int index = 0;
            if (!int.TryParse(countText, out index))
            {
                index = -1;
            }

            obj = GetElementOrDefault(obj, index);

            i++;
        }
        else
        {
            obj = GetFieldOrPropertyValue<object>(part, obj, includeAllBases);
        }
    }
    return (T)obj;
}

// get element from an array or a list
private static object GetElementOrDefault(object arrayOrListObj, int index)
{
    // TReferenceType[] and List<TReferenceType> will come here
    if (arrayOrListObj is IEnumerable<object> referenceItr)
    {
        return referenceItr.ElementAtOrDefault(index);
    }

    // TValueType[] and List<TValueType> will come here
    // (because Array and List<TValueType> both inherits IList)
    var valueItr = arrayOrListObj as System.Collections.IList;
    if (valueItr != null)
    {
        object returnObj = null;
        if (index < 0 || index >= valueItr.Count)
        {
            // out of range (fail safe)
            // create default of Array/List's element type
            Type itrType = valueItr.GetType();
            Type elementType = itrType.IsArray ? itrType.GetElementType() : itrType.GetGenericArguments()[0];
            returnObj = Activator.CreateInstance(elementType);
        }
        else
        {
            returnObj = valueItr[index];
        }

        return returnObj;
    }

    throw new Exception($"can't parse {arrayOrListObj.GetType()} as Array or List");
}
AnnulusGames commented 1 year ago

いただいたコードを元に問題を修正しました。ありがとうございます!