Skip to content

Commit 25350f7

Browse files
committed
Implement TypeSearch with generics
1 parent 105f461 commit 25350f7

File tree

4 files changed

+147
-15
lines changed

4 files changed

+147
-15
lines changed

Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ TypePopupCache GetTypePopup (SerializedProperty property) {
141141

142142
Type baseType = ManagedReferenceUtility.GetType(managedReferenceFieldTypename);
143143
var popup = new AdvancedTypePopup(
144-
TypeMenuUtility.GetTypes(baseType),
144+
TypeSearch.GetTypes(baseType),
145145
k_MaxTypePopupLineCount,
146146
state
147147
);

Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,11 @@
33
using System.Collections.Generic;
44
using UnityEditor;
55

6-
namespace MackySoft.SerializeReferenceExtensions.Editor {
6+
namespace MackySoft.SerializeReferenceExtensions.Editor
7+
{
78
public static class TypeMenuUtility {
89

910
public const string k_NullDisplayName = "<null>";
10-
static readonly Type k_UnityObjectType = typeof(UnityEngine.Object);
11-
12-
public static IEnumerable<Type> GetTypes (Type baseType)
13-
{
14-
return TypeCache.GetTypesDerivedFrom(baseType).Append(baseType).Where(p =>
15-
(p.IsPublic || p.IsNestedPublic || p.IsNestedPrivate) &&
16-
!p.IsAbstract &&
17-
!p.IsGenericType &&
18-
!k_UnityObjectType.IsAssignableFrom(p) &&
19-
Attribute.IsDefined(p, typeof(SerializableAttribute)) &&
20-
!Attribute.IsDefined(p, typeof(HideInTypeMenuAttribute))
21-
);
22-
}
2311

2412
public static AddTypeMenuAttribute GetAttribute (Type type) {
2513
return Attribute.GetCustomAttribute(type,typeof(AddTypeMenuAttribute)) as AddTypeMenuAttribute;
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
using System;
2+
using System.Linq;
3+
using System.Collections.Generic;
4+
using UnityEditor;
5+
using System.Reflection;
6+
7+
namespace MackySoft.SerializeReferenceExtensions.Editor
8+
{
9+
public static class TypeSearch
10+
{
11+
12+
#if UNITY_2023_2_OR_NEWER
13+
static readonly Dictionary<Type, List<Type>> m_TypeCache = new Dictionary<Type, List<Type>>();
14+
#endif
15+
16+
public static IEnumerable<Type> GetTypes (Type baseType)
17+
{
18+
#if UNITY_2023_2_OR_NEWER
19+
// NOTE: This is a workaround for Unity 2023.2 and later.
20+
// 2023.2 because SerializeReference supports generic type instances and because the behaviour is stable.
21+
if (baseType.IsGenericType)
22+
{
23+
return GetTypesWithGeneric(baseType);
24+
}
25+
else
26+
{
27+
return GetTypesUsingTypeCache(baseType);
28+
}
29+
#else
30+
return GetTypesWithGeneric(baseType);
31+
#endif
32+
}
33+
34+
static IEnumerable<Type> GetTypesUsingTypeCache (Type baseType)
35+
{
36+
return TypeCache.GetTypesDerivedFrom(baseType)
37+
.Append(baseType)
38+
.Where(IsValidType);
39+
}
40+
41+
#if UNITY_2023_2_OR_NEWER
42+
static IEnumerable<Type> GetTypesWithGeneric (Type baseType)
43+
{
44+
if (m_TypeCache.TryGetValue(baseType, out List<Type> result))
45+
{
46+
return result;
47+
}
48+
49+
result = new List<Type>();
50+
Type genericTypeDefinition = null;
51+
Type[] targetTypeArguments = null;
52+
Type[] genericTypeParameters = null;
53+
54+
if (baseType.IsGenericType)
55+
{
56+
genericTypeDefinition = baseType.GetGenericTypeDefinition();
57+
targetTypeArguments = baseType.GetGenericArguments();
58+
genericTypeParameters = genericTypeDefinition.GetGenericArguments();
59+
}
60+
else
61+
{
62+
genericTypeDefinition = baseType;
63+
targetTypeArguments = Type.EmptyTypes;
64+
genericTypeParameters = Type.EmptyTypes;
65+
}
66+
67+
IEnumerable<Type> types = AppDomain.CurrentDomain.GetAssemblies()
68+
.SelectMany(x => x.GetTypes())
69+
.Where(IsValidType);
70+
71+
foreach (Type type in types)
72+
{
73+
Type[] interfaceTypes = type.GetInterfaces();
74+
foreach (Type interfaceType in interfaceTypes)
75+
{
76+
if (!interfaceType.IsGenericType || interfaceType.GetGenericTypeDefinition() != genericTypeDefinition)
77+
{
78+
continue;
79+
}
80+
81+
Type[] sourceTypeArguments = interfaceType.GetGenericArguments();
82+
83+
bool allParametersMatch = true;
84+
85+
for (int i = 0; i < genericTypeParameters.Length; i++)
86+
{
87+
var variance = genericTypeParameters[i].GenericParameterAttributes & GenericParameterAttributes.VarianceMask;
88+
89+
Type sourceTypeArgument = sourceTypeArguments[i];
90+
Type targetTypeArgument = targetTypeArguments[i];
91+
92+
if (variance == GenericParameterAttributes.Contravariant)
93+
{
94+
if (!sourceTypeArgument.IsAssignableFrom(targetTypeArgument))
95+
{
96+
allParametersMatch = false;
97+
break;
98+
}
99+
}
100+
else if (variance == GenericParameterAttributes.Covariant)
101+
{
102+
if (!targetTypeArgument.IsAssignableFrom(sourceTypeArgument))
103+
{
104+
allParametersMatch = false;
105+
break;
106+
}
107+
}
108+
else
109+
{
110+
if (sourceTypeArgument != targetTypeArgument)
111+
{
112+
allParametersMatch = false;
113+
break;
114+
}
115+
}
116+
}
117+
118+
if (allParametersMatch)
119+
{
120+
result.Add(type);
121+
break;
122+
}
123+
}
124+
}
125+
126+
m_TypeCache.Add(baseType, result);
127+
return result;
128+
}
129+
#endif
130+
131+
static bool IsValidType (Type type)
132+
{
133+
return
134+
(type.IsPublic || type.IsNestedPublic || type.IsNestedPrivate) &&
135+
!type.IsAbstract &&
136+
!type.IsGenericType &&
137+
!typeof(UnityEngine.Object).IsAssignableFrom(type) &&
138+
Attribute.IsDefined(type, typeof(SerializableAttribute)) &&
139+
!Attribute.IsDefined(type, typeof(HideInTypeMenuAttribute));
140+
}
141+
}
142+
}

Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)