namespace UnityHelpers.Core.Extension { #if UNITY_EDITOR using UnityEditor; using System; using System.Reflection; public static class SerializedPropertyExtensions { /// /// Gets the instance object that contains the given SerializedProperty. /// /// The SerializedProperty. /// Outputs the FieldInfo of the referenced field. /// The instance object that owns the field. public static object GetEnclosingObject( this SerializedProperty property, out FieldInfo fieldInfo ) { fieldInfo = null; object obj = property.serializedObject.targetObject; if (obj == null) { return null; } Type type = obj.GetType(); string[] pathParts = property.propertyPath.Split('.'); // Traverse the path but stop at the second-to-last field for (int i = 0; i < pathParts.Length - 1; ++i) { string fieldName = pathParts[i]; if (string.Equals(fieldName, "Array", StringComparison.Ordinal)) { // Move to "data[i]", no need to length-check, we're guarded above ++i; if ( !int.TryParse( pathParts[i] .Replace("data[", string.Empty, StringComparison.Ordinal) .Replace("]", string.Empty, StringComparison.Ordinal), out int index ) ) { // Unexpected, die fieldInfo = null; return null; } obj = GetElementAtIndex(obj, index); type = obj?.GetType(); continue; } fieldInfo = type?.GetField( fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance ); if (fieldInfo == null) { return null; } // Move deeper but stop before the last property in the path if (i < pathParts.Length - 2) { obj = fieldInfo.GetValue(obj); type = fieldInfo.FieldType; } } return obj; } /// /// Gets the FieldInfo and the instance object that owns the field for a given SerializedProperty. /// /// The SerializedProperty to reflect upon. /// Outputs the FieldInfo of the referenced field. /// The instance object that owns the field. public static object GetTargetObjectWithField( this SerializedProperty property, out FieldInfo fieldInfo ) { fieldInfo = null; object obj = property.serializedObject.targetObject; if (obj == null) { return null; } Type type = obj.GetType(); string[] pathParts = property.propertyPath.Split('.'); for (int i = 0; i < pathParts.Length; ++i) { string fieldName = pathParts[i]; if (string.Equals(fieldName, "Array", StringComparison.Ordinal)) { // Move to "data[i]" ++i; if (pathParts.Length <= i) { break; } if ( !int.TryParse( pathParts[i] .Replace("data[", string.Empty, StringComparison.Ordinal) .Replace("]", string.Empty, StringComparison.Ordinal), out int index ) ) { // Unexpected, die fieldInfo = null; return null; } obj = GetElementAtIndex(obj, index); type = obj?.GetType(); continue; } fieldInfo = type?.GetField( fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance ); if (fieldInfo == null) { return null; } // Move deeper into the object tree obj = fieldInfo.GetValue(obj); type = fieldInfo.FieldType; } return obj; } private static object GetElementAtIndex(object obj, int index) { if (obj is System.Collections.IList list && index >= 0 && index < list.Count) { return list[index]; } return null; } } #endif }