The initialization of my unity components often look like this.

    [RequireComponent(typeof(RigidBody2D))]
    [RequireComponent(typeof(PlayerComponent))]
    public sealed class MyGreatComponent : MonoBehaviour
    {
        private RigidBody2D _rigidBody;

        // This component needs any collider, so RequireComponent doesn't work well when it could be children.
        private Collider2D _collider;

        private PlayerComponent _playerComponent;

        // We don't require a weapon component - so its valid to be null.
        private WeaponComponent _weaponComponent;

        private void Awake()
        {
            _rigidBody = GetComponent<RigidBody2D>();

            _collider = GetComponent<Collider2D>();
            if (_collider == null)
            {
                Debug.LogError("No collision component found!");
            }

            _playerComponent = GetComponent<PlayerComponent>();

            // No need to check for null since it's optional.
            _weaponComponent = GetComponent<WeaponComponent>();
        }

        // Amazing stuff happening below...
    }

And I got tired of the boilerplate in Awake - having to type GetComponent multiple of times and also potentially checking for null each time.

So I made [SiblingComponent].

    [RequireComponent(typeof(WeaponComponent))]
    [RequireComponent(typeof(RigidBody2D))]
    [RequireComponent(typeof(PlayerComponent))]
    public sealed class MyGreatComponent : MonoBehaviour
    {
        [SiblingComponent]
        private RigidBody2D _rigidBody;

        [SiblingComponent]
        private Collider2D _collider;

        [SiblingComponent]
        private PlayerComponent _playerComponent;

        // No error message if we can't find the component.
        [SiblingComponent(Optional = true)]
        private WeaponComponent _weaponComponent;

        private void Awake()
        {
            this.AssignSiblingComponents();
        }

        // Amazing stuff happening below...
    }

[SiblingComponent] automatically looks up a component of the specified type on the same GameObject and assigns it to the member variable. If it doesn’t exist, it’ll print an error. Optional = true suppresses the error if it doesn’t exist.

This streamlined the initialization boilerplate for most of my components and made it much easier to add/remove dependencies on other sibling components.

The Implementation

Create a new file (I called it SiblingComponentAttribute.cs) and let’s define our attribute class

    [AttributeUsage(AttributeTargets.Field)]
    [MeansImplicitUse]
    public sealed class SiblingComponentAttribute : Attribute
    {
        public bool Optional = false;
    }

Next, we’ll create AssignSiblingComponents - the function that iterates through the [SiblingComponent] attributes and looks up the correct component.

I chose to make this an extension method to have the syntax this.AssignSiblingComponents() since I found it the easiest but you can also go the route of a normal function.

public static class SiblingComponentExtensions
{
    // this is a Component since I didn't need it to be a MonoBehaviour. You can change this to a MonoBehaviour if you like.
    public static void AssignSiblingComponents(this Component component)
    {
        // ...
    }
}

First we need to get all fields with our [SiblingComponent] attribute. I like using LINQ for this and making an iterator for it.

public static void AssignSiblingComponents(this Component component)
{
    IEnumerable<FieldInfo> fields = component.GetType()
                                             .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                                             .Where(prop => Attribute.IsDefined(prop, typeof(SiblingComponentAttribute)));

    // ...
}

Next, let’s iterate through each property and try to find the component.

public static void AssignSiblingComponents(this Component component)
{
    // ...

    foreach (FieldInfo field in fields)
    {
        Type fieldType = field.FieldType;

        // I don't support arrays of components with the code below but you can easily implement it with a bit more work.
        if (fieldType.IsArray)
        {
            continue;
        }

        Type componentType = fieldType.IsArray ? fieldType.GetElementType() : fieldType;

        Component[] siblingComponents = component.GetComponents(componentType);

        // ...
    }
}

Once we’ve tried to look up our component, if we’ve found it, assign it. Otherwise print an error if we need to.

public static void AssignSiblingComponents(this Component component)
{
    // ...

    foreach (FieldInfo field in fields)
    {
        // ...

        Component[] siblingComponents = component.GetComponents(componentType);

        if (siblingComponents.Length > 0)
        {
            field.SetValue(component, siblingComponents[0]);
        }
        else if (!field.GetAttribute<SiblingComponentAttribute>().Optional) // Only need to print an error if we're not optional.
        {
            // Feel free to customize this error message.
            Debug.LogError($"{component.gameObject}: {component} - Unable to find sibling component of type {field.FieldType}");
        }
    }
}

And you’re done! Here’s the full AssignSiblingComponents implementation:

public static void AssignSiblingComponents(this Component component)
{
    IEnumerable<FieldInfo> fields = component.GetType()
                                             .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                                             .Where(prop => Attribute.IsDefined(prop, typeof(SiblingComponentAttribute)));

    foreach (FieldInfo field in fields)
    {
        Type fieldType = field.FieldType;
        Type componentType = fieldType.IsArray ? fieldType.GetElementType() : fieldType;

        Component[] siblingComponents = component.GetComponents(componentType);

        if (siblingComponents.Length > 0)
        {
            field.SetValue(component, siblingComponents[0]);
        }
        else if (!field.GetAttribute<SiblingComponentAttribute>().Optional)
        {
            Debug.LogError($"{component.gameObject} {component} - Unable to find sibling component of type {field.FieldType}");
        }
    }
}

Here’s a GitHub Gist of the full implemenetation of SiblingComponentAttribute.as