6

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);
}
  • @GrantWinney Added working unity3d code. –  Aug 08 '17 at 00:46
  • 3
    Speaking about your edited question, most people think that Unity uses .NET Object. It uses `UnityEngine.Object` and this can be confused if you are not paying attention. – Programmer Aug 08 '17 at 01:07

2 Answers2

17

Don't worry too much about this. The if (obj) is only legal in Unity because there is an implicit operator overload in Unity. It is done in the UnityEngine.Object class.

It looks something like this:

  public static implicit operator bool(Object obj)
     {
       return (obj != null);
     }

Unity planed to remove this feature but decided to go against it because it would break every Unity code out there. You can read more about this here.

The very bad side of this feature is that it made it impossible to use the Null-Coalescing operator feature in the Editor. The good side is that it s simplifies checking for null.

Kamran Bigdely
  • 7,946
  • 18
  • 66
  • 86
Programmer
  • 121,791
  • 22
  • 236
  • 328
0

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

Denis Sivtsov
  • 120
  • 1
  • 12