Universal function to check and set components

Hey everyone, what I’m trying to do is create a function I can add to any of my scripts to make sure that everything I need on the gameObject is there and then set it. For example:

I have 3 components I need to set:

Health health;
Animator anim;
NavMeshAgent agent;

Normally to check and set these, I would need to do something like this:

private void CheckAndSetComponents()
        {
            if (GetComponent<Health>() != null)
            {
                health = GetComponent<Health>();
            }
            else
            {
                print("There is no Health.cs script!!!");
            }

            if (GetComponent<NavMeshAgent>() != null)
            {
                agent = GetComponent<NavMeshAgent>();
            }
            else
            {
                print("There is no NavMeshAgent!!!");
            }

            if (GetComponent<Animator>() != null)
            {
                anim = GetComponent<Animator>();
            }
            else
            {
                print("There is no Animator!!!");
            }
        }

But I’m looking for a neater solution. What I’m wanting is a way to pass varying amounts of arguments into a function so they can be checked and set. Something like this:
(I know some of this is wrong, but I just wanted to show where I’m trying to go with this)

 object components[]; 
    void Start() 
    {
        components = {anim, health, agent}
        CheckAndSetComponents(components);
    {
        
    private void CheckAndSetComponents(object[] components )
    {
        foreach(object i in components)
        {
            //Type type = i.GetType();
            if (GetComponent<i.GetType()>() != null)
            {
                 i = GetComponent<Type>();
            }
        }
    }

My biggest obstacle right now seems to be being able to to pass the type to GetComponent dynamically. If I try to use the type variable I created, it says that I’m trying to use a variable instead of a type, and it doesn’t seem to want me to put a method in there to get the type, so I’m a bit stumped on this. Any help with this would be greatly appreciated.

Your approach would not work as you can not call the GetType method on a variable that is not set yet. Also like the compiler told you, you have to use an actual type as generic parameter. You can not use System.Type objects as generic parameters. You could use the GetComponent version that takes a System.Type object reference, however as I said you can not get the type from a null reference. Going this route you would need to use reflection to analyse your own class field types and work with the fieldType. However this wouldn’t be really practical.

I haven’t tried this approach but a method like this should work:

protected void InitComponent<T>(ref T aComponent, bool aAllowCreate = false) where T : Component
{
    // variable is already initialized, so nothing to do here
    if (aComponent != null)
        return;
    
    // Try to get the component. If found we are done.
    aComponent = gameObject.GetComponent<T>();
    if (aComponent != null)
        return;
    
    // if the component reference is still null we either add this component or display
    // a warning depending on the optional "aAllowCreate" parameter.
    if (aAllowCreate)
        aComponent = gameObject.AddComponent<T>();
    else
        Debug.LogWarning("The component " + typeof(T).Name + " is missing on this gameobject", gameObject);
}

With this method you should be able to use it like this:

Health health;
Animator anim;
NavMeshAgent agent;

void Awake()
{
    InitComponent(ref health);
    InitComponent(ref anim);
    InitComponent(ref agent);
}

This method could be defined as an extension method so it’s available globally. You just need to move this method into a static class and change the signature to this:

public static class ComponentInitializeExtension
{
    public void InitComponent<T>(this GameObject gameObject, ref T aComponent, bool aAllowCreate = false) where T : Component
    {
        // [ ... ]
    }
}

To use this variant you have to change the usage to

void Awake()
{
    gameObject.InitComponent(ref health);
    gameObject.InitComponent(ref anim);
    gameObject.InitComponent(ref agent);
}

As I said I haven’t tested this code at all. If you have any issues, feel free to add a comment and I’ll look into it when I have time.