namespace UnityHelpers.Core.Attributes { using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using Extension; using Helper; using JetBrains.Annotations; using UnityEngine; [AttributeUsage(AttributeTargets.Field)] [MeansImplicitUse] public sealed class ParentComponentAttribute : Attribute { public bool optional = false; public bool includeInactive = true; public bool onlyAncestors = false; } public static class ParentComponentExtensions { private static readonly Dictionary< Type, (FieldInfo field, Action setter)[] > FieldsByType = new(); public static void AssignParentComponents(this Component component) { Type componentType = component.GetType(); (FieldInfo field, Action setter)[] fields = FieldsByType.GetOrAdd( componentType, type => { FieldInfo[] fields = type.GetFields( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic ); return fields .Where(field => Attribute.IsDefined(field, typeof(ParentComponentAttribute)) ) .Select(field => (field, ReflectionHelpers.GetFieldSetter(field))) .ToArray(); } ); foreach ((FieldInfo field, Action setter) in fields) { Type fieldType = field.FieldType; bool isArray = fieldType.IsArray; Type parentComponentType = isArray ? fieldType.GetElementType() : fieldType; bool foundParent; ParentComponentAttribute customAttribute = field.GetCustomAttribute(); if (field.GetCustomAttribute().onlyAncestors) { Transform parent = component.transform.parent; if (parent == null) { foundParent = false; } else if (isArray) { Component[] parentComponents = parent.GetComponentsInParent( parentComponentType, customAttribute.includeInactive ); foundParent = 0 < parentComponents.Length; Array correctTypedArray = ReflectionHelpers.CreateArray( parentComponentType, parentComponents.Length ); Array.Copy(parentComponents, correctTypedArray, parentComponents.Length); setter(component, correctTypedArray); } else if ( fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>) ) { parentComponentType = fieldType.GenericTypeArguments[0]; Component[] parents = parent.GetComponentsInParent( parentComponentType, customAttribute.includeInactive ); IList instance = ReflectionHelpers.CreateList( parentComponentType, parents.Length ); foundParent = false; foreach (Component parentComponent in parents) { instance.Add(parentComponent); foundParent = true; } setter(component, instance); } else { Component childComponent = parent.GetComponentInParent( parentComponentType, customAttribute.includeInactive ); foundParent = childComponent != null; if (foundParent) { setter(component, childComponent); } } } else { if (isArray) { Component[] parentComponents = component.GetComponentsInParent( parentComponentType, customAttribute.includeInactive ); foundParent = 0 < parentComponents.Length; Array correctTypedArray = ReflectionHelpers.CreateArray( parentComponentType, parentComponents.Length ); Array.Copy(parentComponents, correctTypedArray, parentComponents.Length); setter(component, correctTypedArray); } else if ( fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>) ) { parentComponentType = fieldType.GenericTypeArguments[0]; Component[] parents = component.GetComponentsInParent( parentComponentType, customAttribute.includeInactive ); IList instance = ReflectionHelpers.CreateList( parentComponentType, parents.Length ); foundParent = false; foreach (Component parentComponent in parents) { instance.Add(parentComponent); foundParent = true; } setter(component, instance); } else { Component childComponent = component.GetComponentInParent( parentComponentType, customAttribute.includeInactive ); foundParent = childComponent != null; if (foundParent) { setter(component, childComponent); } } } if ( !foundParent && field.GetCustomAttributes(typeof(ParentComponentAttribute), false)[0] is ParentComponentAttribute { optional: false } ) { component.LogError($"Unable to find parent component of type {fieldType}"); } } } } }