Implement TypeSearch with generics

This commit is contained in:
Makihiro 2024-10-27 14:57:23 +09:00
parent 105f461ba8
commit 25350f7036
4 changed files with 147 additions and 15 deletions

View File

@ -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
);

View File

@ -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 = "<null>";
static readonly Type k_UnityObjectType = typeof(UnityEngine.Object);
public static IEnumerable<Type> 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;

View File

@ -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<Type, List<Type>> m_TypeCache = new Dictionary<Type, List<Type>>();
#endif
public static IEnumerable<Type> 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<Type> GetTypesUsingTypeCache (Type baseType)
{
return TypeCache.GetTypesDerivedFrom(baseType)
.Append(baseType)
.Where(IsValidType);
}
#if UNITY_2023_2_OR_NEWER
static IEnumerable<Type> GetTypesWithGeneric (Type baseType)
{
if (m_TypeCache.TryGetValue(baseType, out List<Type> result))
{
return result;
}
result = new List<Type>();
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<Type> 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));
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5ea24172520aa2646954c1d246e7ba5d