diff --git a/Assets/BetterExtensions/Editor/Utility/SerializedPropertyUtility.cs b/Assets/BetterExtensions/Editor/Utility/SerializedPropertyUtility.cs index 5a1fc2c..cc4da1f 100644 --- a/Assets/BetterExtensions/Editor/Utility/SerializedPropertyUtility.cs +++ b/Assets/BetterExtensions/Editor/Utility/SerializedPropertyUtility.cs @@ -66,15 +66,14 @@ public static bool GetTypeFromManagedReferenceFullTypeName(string managedReferen public static CachedFieldInfo GetFieldInfoFromPropertyPath(Type type, string propertyPath) { + var arrayElement = ArrayDataWithIndexRegexAny.IsMatch(propertyPath); + propertyPath = ArrayDataWithIndexRegex.Replace(propertyPath, ArrayElementDotName); var cache = new CacheKey(type, propertyPath); if (FieldInfoFromPropertyPathCache.TryGetValue(cache, out var fieldInfoCache)) { return fieldInfoCache; } - - var arrayElement = ArrayDataWithIndexRegexAny.IsMatch(propertyPath); - propertyPath = ArrayDataWithIndexRegex.Replace(propertyPath, ArrayElementDotName); if (FieldInfoFromPropertyPath(propertyPath, ref type, out var fieldInfo)) { diff --git a/Assets/BetterExtensions/Runtime/BetterExtensions.Runtime.asmdef b/Assets/BetterExtensions/Runtime/BetterExtensions.Runtime.asmdef index 97700cd..8e5ccc9 100644 --- a/Assets/BetterExtensions/Runtime/BetterExtensions.Runtime.asmdef +++ b/Assets/BetterExtensions/Runtime/BetterExtensions.Runtime.asmdef @@ -1,7 +1,9 @@ { "name": "BetterExtensions.Runtime", "rootNamespace": "Better.Extensions.Runtime", - "references": [], + "references": [ + "GUID:01df13aca8d01e24a911bcc3e8277031" + ], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, diff --git a/Assets/BetterExtensions/Runtime/Extensions/TypeExtensions.cs b/Assets/BetterExtensions/Runtime/Extensions/TypeExtensions.cs index f666a0a..277da06 100644 --- a/Assets/BetterExtensions/Runtime/Extensions/TypeExtensions.cs +++ b/Assets/BetterExtensions/Runtime/Extensions/TypeExtensions.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using Better.Internal.Core.Runtime; using UnityObject = UnityEngine.Object; namespace Better.Extensions.Runtime @@ -136,7 +137,7 @@ public static IEnumerable GetAllInheritedTypesWithoutUnityObject(this Type return self.GetAllInheritedTypes(unityObjectType); } - + public static bool IsAnonymous(this Type type) { if (type.IsClass && type.IsSealed && type.Attributes.HasFlag(TypeAttributes.NotPublic)) @@ -150,7 +151,7 @@ public static bool IsAnonymous(this Type type) return false; } - + public static IEnumerable GetAllInheritedTypesOfRawGeneric(this Type self) { if (self == null) @@ -233,7 +234,7 @@ public static bool IsSubclassOfRawGeneric(this Type self, Type genericType) return false; } - + public static bool IsSubclassOfAnyRawGeneric(this Type self, IEnumerable genericTypes) { if (self == null) @@ -289,5 +290,74 @@ public static bool IsNullable(this Type self) return Nullable.GetUnderlyingType(self) != null; } + + public static IEnumerable GetMembersRecursive(this Type type) + { + if (type == null) + { + DebugUtility.LogException(nameof(type)); + return Enumerable.Empty(); + } + + var members = new HashSet(new MemberInfoComparer()); + + const BindingFlags methodFlags = Defines.MethodFlags & ~BindingFlags.DeclaredOnly; + do + { + // If the type is a constructed generic type, get the members of the generic type definition + var typeToReflect = type.IsGenericType && !type.IsGenericTypeDefinition ? type.GetGenericTypeDefinition() : type; + + foreach (var member in typeToReflect.GetMembers(methodFlags)) + { + // For generic classes, convert members back to the constructed type + var memberToAdd = type.IsGenericType && !type.IsGenericTypeDefinition + ? ConvertToConstructedGenericType(member, type) + : member; + + if (memberToAdd != null) + { + members.Add(memberToAdd); + } + } + + type = type.BaseType; + } while (type != null); // Continue until you reach the top of the inheritance hierarchy + + return members; + } + + private static MemberInfo ConvertToConstructedGenericType(MemberInfo memberInfo, Type constructedType) + { + // Ensure the member's declaring type is a generic type definition + if (memberInfo.DeclaringType != null && memberInfo.DeclaringType.IsGenericTypeDefinition) + { + var members = constructedType.GetMember(memberInfo.Name); + return members.FirstOrDefault(); + } + + // Return the original memberInfo if it's not a property of a generic type definition or doesn't need to be constructed + return memberInfo; + } + + public static MemberInfo GetMemberByNameRecursive(this Type type, string memberName) + { + if (type == null) + { + DebugUtility.LogException(nameof(type)); + return null; + } + + if (string.IsNullOrEmpty(memberName)) + { + DebugUtility.LogException(nameof(memberName)); + return null; + } + + var allMembers = GetMembersRecursive(type); + + // Use LINQ to find the member by name. This assumes you want the first match if there are multiple members with the same name (overloads). + // If you expect overloads and want to handle them differently, you might need a more complex approach. + return allMembers.FirstOrDefault(m => m.Name == memberName); + } } } \ No newline at end of file diff --git a/Assets/BetterExtensions/Runtime/Helpers/Compares.meta b/Assets/BetterExtensions/Runtime/Helpers/Compares.meta new file mode 100644 index 0000000..2572b7a --- /dev/null +++ b/Assets/BetterExtensions/Runtime/Helpers/Compares.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9ff896f34b0248c9ad886bbd1e4bd514 +timeCreated: 1709409412 \ No newline at end of file diff --git a/Assets/BetterExtensions/Runtime/Helpers/Compares/MemberInfoComparer.cs b/Assets/BetterExtensions/Runtime/Helpers/Compares/MemberInfoComparer.cs new file mode 100644 index 0000000..977f4c6 --- /dev/null +++ b/Assets/BetterExtensions/Runtime/Helpers/Compares/MemberInfoComparer.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Reflection; + +namespace Better.Extensions.Runtime +{ + internal class MemberInfoComparer : IEqualityComparer + { + public bool Equals(MemberInfo x, MemberInfo y) + { + if (ReferenceEquals(x, y)) return true; + if (ReferenceEquals(x, null) || ReferenceEquals(y, null)) return false; + return x.Name == y.Name && x.DeclaringType == y.DeclaringType; + } + + public int GetHashCode(MemberInfo obj) + { + unchecked + { + var hashCode = obj.Name.GetHashCode(); + if (obj.DeclaringType != null) hashCode = (hashCode * 397) ^ obj.DeclaringType.GetHashCode(); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/Assets/BetterExtensions/Runtime/Helpers/Compares/MemberInfoComparer.cs.meta b/Assets/BetterExtensions/Runtime/Helpers/Compares/MemberInfoComparer.cs.meta new file mode 100644 index 0000000..e43db0b --- /dev/null +++ b/Assets/BetterExtensions/Runtime/Helpers/Compares/MemberInfoComparer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6bca15129e59431f8e825e5fe89bb03e +timeCreated: 1709409376 \ No newline at end of file diff --git a/Assets/BetterExtensions/package.json b/Assets/BetterExtensions/package.json index 6c5dfef..d17c143 100644 --- a/Assets/BetterExtensions/package.json +++ b/Assets/BetterExtensions/package.json @@ -1,7 +1,7 @@ { "name": "com.uurha.betterextensions", "displayName": "Better Extensions", - "version": "1.5.7", + "version": "1.5.8", "unity": "2021.3", "description": "Unity extensions, serialize extension, async extension, string extension and UI extensions", "dependencies": {