Why do if statements in Unity3d C# allow objects that are not bools?

Viewed 552

In the standard .NET 4.6 compiler, the following if statement is not legal, you will get the compiler error: CS0029 Cannot implicitly convert type 'UserQuery.TestClass' to 'bool'. This is all well and good, and I understand this.

void Main()
{
    TestClass foo = new TestClass();

    if(foo) 
    {
        "Huh. Who knew?".Dump();
    }
}


public class TestClass : Object
{
}

However, in Unity3d this code is perfectly legal and I have seen several examples even encouraging this Javascript style of null checking.

What's going on here and is it possible to leverage this in my non-Unity3d work in .NET?

Here is an example of legal code in Unity3D:

using UnityEngine;

public class SampleIfBehavior : MonoBehaviour
{

    // Use this for initialization
    void Start ()
    {
        var obj = new SampleIfBehavior();

        if (obj)
        {
            Console.Write("It wasn't null.");
        }
    }
}

The plot thickens! If I attempt to compare an object that doesn't derive from mono behavior, I get the expected compiler warning.

public class TestClass
{
}
void Start ()
{
    var obj2 = new TestClass();
    if (obj2) // cast from TestClass to bool compiler error
    {

    }
}

Thanks to Programmer, I dug down all the way to Unity3D's Object (I overlooked it because I mistakenly assumed it was .NET's actual object.)

Relevant code for the curious (in UnityEngine.Object):

public static implicit operator bool(Object exists)
{
  return !Object.CompareBaseObjects(exists, (Object) null);
}

public static bool operator ==(Object x, Object y)
{
  return Object.CompareBaseObjects(x, y);
}

public static bool operator !=(Object x, Object y)
{
  return !Object.CompareBaseObjects(x, y);
}
2 Answers

I read many articles on this theme include mentioned by @Programmer and other on stackoverflow. The root of possible problems is clear, especial in case of deleting GameObject (by example) and after this testing for its existence using "!=null" or "!((bool)GameObject)". I decide to make simple check for that issue (Unity 2019.4.14 LTS): One GameObject, which will be deleted, have script

    public class CheckDestroy : MonoBehaviour
    {
        public OnGUITest linkToMangerObject;
    
        private void OnMouseDown()
        {
            linkToMangerObject.InitCount();
            Destroy(gameObject);
        }
    }

Other GameObject, which test existence of deleted object:

   public class OnGUITest : MonoBehaviour
   {
        private int externalCount;
        private bool flagObjectDeleted = false;
        public GameObject deletedObject;
    
        void OnGUI()
        {
           if (flagObjectDeleted)
                if  (deletedObject)
                //if (deletedObject != null)
                    externalCount++;
                else
                    print($"Detected the object deleted, count={externalCount}");
        }
    
        public void InitCount()
        {
            externalCount = 0;
            flagObjectDeleted = true;
        }
    }

The result is that the validating object immediately detects the destroy of other object. It's a synthetic test with maximum simple scene and with minimum loading on CPU/Mem and on Garbage collections.

Maybe in a real case (under a real load) it will work differently or may be Native code was changed - added additionally checks?

enter image description here

Related