Skip to content

Commit b4370b8

Browse files
committed
"Inherits" attribute now accepts BaseType<>
"Inherits" attribute now accepts BaseType<> and can detect all derivatives of BaseType<T> without the concrete type parameter
1 parent 64d7bf5 commit b4370b8

3 files changed

Lines changed: 105 additions & 4 deletions

File tree

Dependencies/SolidUtilities/Runtime/Extensions/TypeExtensions.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
namespace SolidUtilities.Extensions
22
{
33
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
46
using System.Reflection;
7+
using UnityEngine;
58

69
/// <summary>Different useful extensions for <see cref="System.Type"/>.</summary>
710
public static class TypeExtensions
@@ -30,5 +33,89 @@ public static FieldInfo GetFieldAtPath(this Type parentType, string path)
3033

3134
return field;
3235
}
36+
37+
/// <summary>
38+
/// Collects all the serializable fields of a class: private ones with SerializeField attribute and public ones.
39+
/// </summary>
40+
/// <param name="type">Class type to collect the fields from.</param>
41+
/// <returns>Collection of the serializable fields of a class.</returns>
42+
/// <example><code>
43+
/// var fields = objectType.GetSerializedFields();
44+
/// foreach (var field in fields)
45+
/// {
46+
/// string fieldLabel = ObjectNames.NicifyVariableName(field.Name);
47+
/// object fieldValue = field.GetValue(serializedObject);
48+
/// object newValue = DrawField(fieldLabel, fieldValue);
49+
/// field.SetValue(serializedObject, newValue);
50+
/// }
51+
/// </code></example>
52+
public static IEnumerable<FieldInfo> GetSerializedFields(this Type type)
53+
{
54+
const BindingFlags instanceFilter = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
55+
var instanceFields = type.GetFields(instanceFilter);
56+
return instanceFields.Where(field => field.IsPublic || field.GetCustomAttribute<SerializeField>() != null);
57+
}
58+
59+
/// <summary>Checks whether the type is nullable.</summary>
60+
/// <param name="type">The type to check.</param>
61+
/// <returns>True if the type is nullable.</returns>
62+
public static bool IsNullable(this Type type)
63+
{
64+
return ! type.IsValueType || Nullable.GetUnderlyingType(type) != null;
65+
}
66+
67+
/// <summary>
68+
/// Checks whether the type is derivative of a generic class without specifying its type parameter.
69+
/// </summary>
70+
/// <param name="typeToCheck">The type to check.</param>
71+
/// <param name="generic">The generic class without type parameter.</param>
72+
/// <returns>True if the type is subclass of the generic class.</returns>
73+
/// <example><code>
74+
/// class Base&lt;T> { }
75+
/// class IntDerivative : Base&lt;int> { }
76+
/// class StringDerivative : Base&lt;string> { }
77+
///
78+
/// bool intIsSubclass = typeof(IntDerivative).IsSubclassOfRawGeneric(typeof(Base&lt;>)); // true
79+
/// bool stringIsSubclass = typeof(StringDerivative).IsSubclassOfRawGeneric(typeof(Base&lt;>)); // true
80+
/// </code></example>
81+
public static bool IsSubclassOfRawGeneric(this Type typeToCheck, Type generic)
82+
{
83+
while (typeToCheck != null && typeToCheck != typeof(object))
84+
{
85+
Type cur = typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck;
86+
87+
if (generic == cur)
88+
return true;
89+
90+
typeToCheck = typeToCheck.BaseType;
91+
}
92+
93+
return false;
94+
}
95+
96+
/// <summary>
97+
/// Checks whether the type inherits from the base type.
98+
/// </summary>
99+
/// <param name="typeToCheck">The type to check.</param>
100+
/// <param name="baseType">
101+
/// The base type to check inheritance from. It can be a generic type without the type parameter.
102+
/// </param>
103+
/// <returns>Whether <paramref name="typeToCheck"/>> inherits <paramref name="baseType"/>.</returns>
104+
/// <example><code>
105+
/// class Base&lt;T> { }
106+
/// class IntDerivative : Base&lt;int> { }
107+
///
108+
/// bool isAssignableWithTypeParam = typeof(typeof(Base&lt;int>).IsAssignableFrom(IntDerivative)); // true
109+
/// bool isAssignableWithoutTypeParam = typeof(typeof(Base&lt;>)).IsAssignableFrom(IntDerivative); // false
110+
/// bool inherits = typeof(IntDerivative).Inherits(typeof(Base&lt;>)); // true
111+
/// </code></example>
112+
public static bool InheritsFrom(this Type typeToCheck, Type baseType)
113+
{
114+
bool subClassOfRawGeneric = false;
115+
if (baseType.IsGenericType)
116+
subClassOfRawGeneric = typeToCheck.IsSubclassOfRawGeneric(baseType);
117+
118+
return baseType.IsAssignableFrom(typeToCheck) || subClassOfRawGeneric;
119+
}
33120
}
34121
}

Runtime/Attributes/InheritsAttribute.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace TypeReferences
22
{
33
using System;
4+
using SolidUtilities.Extensions;
45

56
/// <summary>
67
/// Constraint that allows selection of types that inherit a specific parent type or interface when
@@ -51,7 +52,7 @@ public override bool MatchesRequirements(Type type)
5152

5253
bool passesAbstractConstraint = AllowAbstract || !type.IsAbstract;
5354

54-
return BaseType.IsAssignableFrom(type) && passesAbstractConstraint && base.MatchesRequirements(type);
55+
return type.InheritsFrom(BaseType) && passesAbstractConstraint && base.MatchesRequirements(type);
5556
}
5657
}
5758
}

Runtime/TypeReferences.asmdef

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1-
{
2-
"name": "TypeReferences"
3-
}
1+
{
2+
"name": "TypeReferences",
3+
"rootNamespace": "",
4+
"references": [
5+
"GUID:b73ef07971851e14fa74ae50b76f8225"
6+
],
7+
"includePlatforms": [],
8+
"excludePlatforms": [],
9+
"allowUnsafeCode": false,
10+
"overrideReferences": false,
11+
"precompiledReferences": [],
12+
"autoReferenced": true,
13+
"defineConstraints": [],
14+
"versionDefines": [],
15+
"noEngineReferences": false
16+
}

0 commit comments

Comments
 (0)