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