'Unity: Generic Method to get or add a component

Every time I make a new Script in Unity, I always end up doing a bunch of checks for any components my script depends on, like:

SpriteRenderer sr = gameObject.GetComponent<SpriteRenderer>();

if(sr == null)
      sr = gameObject.AddComponent<SpriteRenderer>() as SpriteRenderer;

So I decided to play around and make a generic method to do this with any Component, and I came up with the following code, which I simply added right to my own new Script.

  T GetOrAddComponent<T>() where T : Component
    {
        T component = gameObject.GetComponent<T>();

        if (component == null)     
            component = gameObject.AddComponent<T>() as T;

        return component;

    }

I've tried it and the function works well, but I'm wondering if there is any major downside to this approach, or if there is some other better way to complete the same task, possibly using a existing method I am unaware of or by creating the method in some other way.



Solution 1:[1]

The GetComponent method is actually rather taxing on the system. If you are developing for mobile, too many of them can cause a noticeable lag in load times.

I personally use RequireComponent method. Then the script would automatically add the component the moment it's added to the GameObject. Then I would manually drag the name of the component to the field on the script object in the inspector.

Example:

using UnityEngine;

// PlayerScript requires the GameObject to have a RigidBody component
[RequireComponent (typeof (RigidBody))]
public class PlayerScript : MonoBehavior {
    [SerializeField]
    private RigidBody rb;
...
}

Solution 2:[2]

You could do it all in one if statement if you'd like:

SpriteRenderer sr;

if(gameObject.GetComponent<SpriteRenderer>())
    sr = gameObject.GetComponent<SpriteRenderer>();
else
    sr = gameObject.AddComponent<SpriteRenderer>() as SpriteRenderer;

You will potentially be making the GetComponent call twice, but if you like the code structure better so be it. I personally think the way you are doing it is best though.

Though to be even more concise, you can return the component directly instead of having to store it in the variable component

public T GetOrAddComponent<T>() where T : Component
{
    if (gameObject.GetComponent<T>())
        return gameObject.GetComponent<T>();
    else     
        return gameObject.AddComponent<T>() as T;
}

And as long as you are doing this at scene load or in the start function, there really won't be any performance difference.

Solution 3:[3]

First i recommend you to use "RequireComponent" :

[RequireComponent(typeof(SpriteRenderer))]
[RequireComponent(typeof(AnotherComponent))]
public class NetworkService : MonoBehaviour
{ 
   private SpriteRenderer _sRenderer;

   //strong way to never get a null reference.
   #if UNITY_EDITOR
   private void OnValidate()
   {
       if (null == _sRenderer)
           _sRenderer = GetComponent<_sRenderer>();
   }

   #endif

the "OnValidate" method will be executed every time you make a change in the value of a parameter in the editor or load the script/component.

Solution 4:[4]

public static void GetOrAddComponent<T>(this GameObject model, Action<T> onSucess) where T: Component {
    onSucess(model.GetComponent<T>() ?? model.AddComponent<T> () as T);
}

Solution 5:[5]

In the new version of Unity (With C# 8.0 and above) you can simply do this in one line.

[SerializeField] private SpriteRenderer spriteRenderer;
public SpriteRenderer GetSpriteRenderer => spriteRenderer ??= GetComponent<SpriteRenderer>() ?? gameObject.AddComponent<SpriteRenderer>();

The code above does three things.

  • Returns private local spriteRenderer component from the getter property if it's not null.
  • If spriteRenderer is null, then it proceed to assign the value by calling GetComponent() before returning.
  • If GetComponent() returns null, then the next operation will add sprite renderer component to the game object and assign to spriteRenderer before returning.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Okomikeruko
Solution 2 Xander Luciano
Solution 3 Pablo Pizarro
Solution 4 Josef
Solution 5 Skibur