From 25350f7036a64f389547b4a0290e9982f1aaae08 Mon Sep 17 00:00:00 2001 From: Makihiro Date: Sun, 27 Oct 2024 14:57:23 +0900 Subject: [PATCH] Implement TypeSearch with generics --- .../Editor/SubclassSelectorDrawer.cs | 2 +- .../Editor/TypeMenuUtility.cs | 16 +- .../Editor/TypeSearch.cs | 142 ++++++++++++++++++ .../Editor/TypeSearch.cs.meta | 2 + 4 files changed, 147 insertions(+), 15 deletions(-) create mode 100644 Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs create mode 100644 Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs.meta diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs index 8c0e3f3..b99588f 100644 --- a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs @@ -141,7 +141,7 @@ namespace MackySoft.SerializeReferenceExtensions.Editor Type baseType = ManagedReferenceUtility.GetType(managedReferenceFieldTypename); var popup = new AdvancedTypePopup( - TypeMenuUtility.GetTypes(baseType), + TypeSearch.GetTypes(baseType), k_MaxTypePopupLineCount, state ); diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs index 28d74b3..354d9d4 100644 --- a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs @@ -3,23 +3,11 @@ using System.Linq; using System.Collections.Generic; using UnityEditor; -namespace MackySoft.SerializeReferenceExtensions.Editor { +namespace MackySoft.SerializeReferenceExtensions.Editor +{ public static class TypeMenuUtility { public const string k_NullDisplayName = ""; - static readonly Type k_UnityObjectType = typeof(UnityEngine.Object); - - public static IEnumerable GetTypes (Type baseType) - { - return TypeCache.GetTypesDerivedFrom(baseType).Append(baseType).Where(p => - (p.IsPublic || p.IsNestedPublic || p.IsNestedPrivate) && - !p.IsAbstract && - !p.IsGenericType && - !k_UnityObjectType.IsAssignableFrom(p) && - Attribute.IsDefined(p, typeof(SerializableAttribute)) && - !Attribute.IsDefined(p, typeof(HideInTypeMenuAttribute)) - ); - } public static AddTypeMenuAttribute GetAttribute (Type type) { return Attribute.GetCustomAttribute(type,typeof(AddTypeMenuAttribute)) as AddTypeMenuAttribute; diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs new file mode 100644 index 0000000..75009cf --- /dev/null +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs @@ -0,0 +1,142 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using UnityEditor; +using System.Reflection; + +namespace MackySoft.SerializeReferenceExtensions.Editor +{ + public static class TypeSearch + { + +#if UNITY_2023_2_OR_NEWER + static readonly Dictionary> m_TypeCache = new Dictionary>(); +#endif + + public static IEnumerable GetTypes (Type baseType) + { +#if UNITY_2023_2_OR_NEWER + // NOTE: This is a workaround for Unity 2023.2 and later. + // 2023.2 because SerializeReference supports generic type instances and because the behaviour is stable. + if (baseType.IsGenericType) + { + return GetTypesWithGeneric(baseType); + } + else + { + return GetTypesUsingTypeCache(baseType); + } +#else + return GetTypesWithGeneric(baseType); +#endif + } + + static IEnumerable GetTypesUsingTypeCache (Type baseType) + { + return TypeCache.GetTypesDerivedFrom(baseType) + .Append(baseType) + .Where(IsValidType); + } + +#if UNITY_2023_2_OR_NEWER + static IEnumerable GetTypesWithGeneric (Type baseType) + { + if (m_TypeCache.TryGetValue(baseType, out List result)) + { + return result; + } + + result = new List(); + Type genericTypeDefinition = null; + Type[] targetTypeArguments = null; + Type[] genericTypeParameters = null; + + if (baseType.IsGenericType) + { + genericTypeDefinition = baseType.GetGenericTypeDefinition(); + targetTypeArguments = baseType.GetGenericArguments(); + genericTypeParameters = genericTypeDefinition.GetGenericArguments(); + } + else + { + genericTypeDefinition = baseType; + targetTypeArguments = Type.EmptyTypes; + genericTypeParameters = Type.EmptyTypes; + } + + IEnumerable types = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(x => x.GetTypes()) + .Where(IsValidType); + + foreach (Type type in types) + { + Type[] interfaceTypes = type.GetInterfaces(); + foreach (Type interfaceType in interfaceTypes) + { + if (!interfaceType.IsGenericType || interfaceType.GetGenericTypeDefinition() != genericTypeDefinition) + { + continue; + } + + Type[] sourceTypeArguments = interfaceType.GetGenericArguments(); + + bool allParametersMatch = true; + + for (int i = 0; i < genericTypeParameters.Length; i++) + { + var variance = genericTypeParameters[i].GenericParameterAttributes & GenericParameterAttributes.VarianceMask; + + Type sourceTypeArgument = sourceTypeArguments[i]; + Type targetTypeArgument = targetTypeArguments[i]; + + if (variance == GenericParameterAttributes.Contravariant) + { + if (!sourceTypeArgument.IsAssignableFrom(targetTypeArgument)) + { + allParametersMatch = false; + break; + } + } + else if (variance == GenericParameterAttributes.Covariant) + { + if (!targetTypeArgument.IsAssignableFrom(sourceTypeArgument)) + { + allParametersMatch = false; + break; + } + } + else + { + if (sourceTypeArgument != targetTypeArgument) + { + allParametersMatch = false; + break; + } + } + } + + if (allParametersMatch) + { + result.Add(type); + break; + } + } + } + + m_TypeCache.Add(baseType, result); + return result; + } +#endif + + static bool IsValidType (Type type) + { + return + (type.IsPublic || type.IsNestedPublic || type.IsNestedPrivate) && + !type.IsAbstract && + !type.IsGenericType && + !typeof(UnityEngine.Object).IsAssignableFrom(type) && + Attribute.IsDefined(type, typeof(SerializableAttribute)) && + !Attribute.IsDefined(type, typeof(HideInTypeMenuAttribute)); + } + } +} \ No newline at end of file diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs.meta b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs.meta new file mode 100644 index 0000000..4198c09 --- /dev/null +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5ea24172520aa2646954c1d246e7ba5d \ No newline at end of file