Why is this generic "Get component if null" code not working

I find myself often doing this kind of pattern:

AudioSource cachedAudioSource = null;
AudioSource audioSource
{
    get
    {
        if( cachedAudioSource == null )
        {
            cachedAudioSource = (AudioSource)GetComponent( typeof( AudioSource ) );
        }
        return cachedAudioSource;
    }
}

That gets me the components the first time I need them; no more, no less.

So I thought I could use generics to clean up this code a little bit:

public static void GetComponentIfNull< T >( MonoBehaviour that, ref T cachedT ) where T : Component
{
    if( cachedT == null )
    {
        cachedT = (T)that.GetComponent( typeof( T ) );
        if( cachedT == null )
        {
            Debug.LogWarning( "GetComponent of type " + typeof( T ) + " failed on " + that.name, that );
        }
    }
}

Which results in my client code looking a little neater:

AudioSource cachedAudioSource = null;
AudioSource audioSource
{
    get
    {
        Util.GetComponentIfNull( this, ref cachedAudioSource );
        return cachedAudioSource;
    }
}

But the problem is, it doesn't seem to work. Specifically, the check to see if cachedT == null in the generic function seems to think the ref pointer to my T object is NOT null (even if I explicitly set it to null, which shouldn't be necessary), yet if I check to see if audioSource == null, that returns true.

More specifically, it seems to only happen with built in types that don't derive from MonoBehaviour, i.e. audio sources colliders, etc. I've used this script successfully before for my own scripts.

Is this just something I can't work around due to the fact that apparently nulls aren't real nulls?

Edit: Thanks to @SpikeX for pointing out that Component.audio basically does what I want with my specific example, but I'm still somewhat curious as to why it doesn't work.

If you return the value instead, it'll skip the crazy behaviour you're seeing with the ref (And heck, added an overload now so you can pass any component or gameobject and it still works):

public static T GetComponentIfNull< T >( Component that, T cachedT ) where T : Component
{
    if( cachedT == null )
    {
        cachedT = (T)that.GetComponent( typeof( T ) );
        if( cachedT == null )
        {
            Debug.LogWarning( "GetComponent of type " + typeof( T ) + " failed on " + that.name, that );
        }
    }

    return cachedT;
}

public static T GetComponentIfNull< T >( GameObject that, T cachedT ) where T : Component
{
    if( cachedT == null )
    {
        cachedT = (T)that.GetComponent( typeof( T ) );
        if( cachedT == null )
        {
            Debug.LogWarning( "GetComponent of type " + typeof( T ) + " failed on " + that.name, that );
        }
    }

    return cachedT;
}

And implemented in your other code similar to before:

AudioSource cachedAudioSource = null;
AudioSource audioSource
{
    get
    {
        cachedAudioSource = Util.GetComponentIfNull( this, cachedAudioSource );
        return cachedAudioSource;
    }
}

I've actually noticed the same thing in my own project (Though with textures instead of components) - it seems like unity's UnityEngine.Object references work a little wonky with being passed as null to ref parameters

The ref keyword requires the variable to be initialized before it can be passed, and (I believe) will do so automatically if it's found to be null. You want to use the out keyword instead.

You also have this method header:

public static void GetComponentIfNull< T >( MonoBehaviour that, ref T cachedT ) where T : Component

But you call the method like this:

Util.GetComponentIfNull( this, ref cachedAudioSource );

I think you mean to call the method like this:

Util.GetComponentIfNull<AudioSource>(this, ref cachedAudioSource);

Also, kind of a side note, why are you even bothering with this? MonoBehaviours maintain a list of common component references anyway, that basically do exactly what you're already doing (so essentially you're reinventing the wheel). Check out the Inherited Members section of the MonoBehaviour doc page which lists all the references that are built into the MonoBehaviour class. Checking these for null is a lot less costly than using GetComponent() on them, even if only once. So, for example, instead of using the code you produced, just check if the inherited property `audio` is null or not. This is an automatically-maintained reference to the AudioSource component attached to this game object.

I do recognize that your method would work for any custom component type that isn't included as a reference in the MonoBehaviour class, though, and in those instances, it would be appropriate.

Hello? Thankyous for Null code: link text
There are some classes that don’t have that in them. It really doesn’t matter, in this case, whether or not I’m using ref vs. out. Since I’m using them for a reference type

Hey ! Thankyous For me Nullcode :http://nullcodes.com/nulled-scripts/
There are some classes that don’t have that in them. It really doesn’t matter, in this case, whether or not I’m using ref vs. out. Since I’m using them for a reference type

  • Null codes - Null script

Any Unity Component Check null or not. [Not a Generic way but its help to you]

  1. Create Script :
    InternalComponentExtension.cs

    public class InternalComponentExtension : MonoBehaviour
    {
    [SerializeField]private static TextMeshProUGUI textMeshProUGUI;
    [SerializeField]public static TextMeshProUGUI TextMeshProUGUI
    {
    get{
    if(textMeshProUGUI == null)
    {
    GameObject g1 = new GameObject();
    textMeshProUGUI = g1.AddComponent();
    }
    else
    {
    Debug.LogWarning("Please Pass Orignal Component "+typeof(TextMeshProUGUI));
    }
    return textMeshProUGUI;
    }
    }

         [SerializeField]private static AudioSource audioSource;
         [SerializeField]public static AudioSource AudioSource
         {
             get{
                 if(audioSource == null)
                 {
                     GameObject g1 = new GameObject();
                     audioSource = g1.AddComponent<AudioSource>();
                 }
                  else
                 {
                     Debug.LogWarning("Please Pass Orignal Component "+typeof(AudioSource));
                 }
                 return audioSource;
             }
         }
     }
    
  2. Access or Check Component Create Script : Demo.cs and add in to any GameObject in scene.

    public class Demo: MonoBehaviour
    {
    [SerializeField]private TextMeshProUGUI cookingNote;
    [SerializeField]private TextMeshProUGUI CookingNote
    {
    get
    {
    if(cookingNote == null)
    {
    cookingNote = InternalComponentExtension.TextMeshProUGUI;
    }
    return cookingNote;
    }
    }
    [SerializeField]private AudioSource audioSource;
    [SerializeField]private AudioSource AudioSource
    {
    get
    {
    if(audioSource == null)
    {
    audioSource = InternalComponentExtension.AudioSource;
    }
    return audioSource;
    }
    }
    void Start()
    {
    //Access any Renderer or TextMeshProUGUI property.
    CookingNote.text = “It is Null”;
    AudioSource.volume = 0.5f;
    }
    }