skip to Main Content

I wrote the following function to have a handy utility for sanity checks in my unity projects.

namespace MyProject.Utilities
{
    public class DebugUtils
    {
        /// <summary>
        /// Checks if passed object is null and prints to console if it is.
        /// </summary>
        /// <param name="obj">The obj.</param>
        public static void DebugAssertNull<T>(T obj, string tag = "M_UTILS_DEBUG")
        {
            if (obj == null) 
            {
                StackTrace stackTrace = new();
                StackFrame stackFrame = stackTrace.GetFrame(1);
                string methodName = stackFrame.GetMethod().Name;
                string fileName = stackFrame.GetFileName();
                int lineNumber = stackFrame.GetFileLineNumber();
                string message = $"[{tag}] Object of type {typeof(T)} is null in method {methodName} at {fileName}:{lineNumber}";
                UnityEngine.Debug.Assert(false, message);
            }
        }
    }
}

Now when I call this function from another script as follows

public class SomeBehaviour: MonoBehaviour{
    public GameObject someObject;
    
    void Start(){
        DebugAssertNull<GameObject>(someObject);
    }

}

Now I am expecting that a message prints to the console. However upon running this code obj turns out to be not null. Using the debugger in Visual Studio 2019, setting up a watch obj == null evaluates to true.

I feel like there is some implementation detail I am missing here. I am working with unity 2021, Visual Studio 2019, and .NET Framework 4.

2

Answers


  1. It’s an old Unity… hmm… let’s call it a "feature" =)

    The reason is that real Unity objects are in the C++ part and C# objects are only wrappers for these objects. That’s why the basic Unity3d object overrides == operator and returns true in case of comparing with null even object still exists.

    You can find more information here

    Login or Signup to reply.
  2. Just in addition to this answer

    It works for me if you explicitly limit the type like e.g.

    public static void DebugAssertNull<T>(T obj, string tag = "M_UTILS_DEBUG") where T : UnityEngine.Object
    {
        if (obj == null) 
        {
            StackTrace stackTrace = new();
            StackFrame stackFrame = stackTrace.GetFrame(1);
            string methodName = stackFrame.GetMethod().Name;
            string fileName = stackFrame.GetFileName();
            int lineNumber = stackFrame.GetFileLineNumber();
            string message = $"[{tag}] Object of type {typeof(T)} is null in method {methodName} at {fileName}:{lineNumber}";
            UnityEngine.Debug.Assert(false, message);
        }
    }
    

    so apparently generics work different from what I would’ve expected as well and treat T like an object. In the upper it definitely uses the overload UnityEngine.Object ==.

    If this is intended to be used for stuff referenced in the Unity Inspector it should also be enough.

    The only thing going by reference in the Inspector is UnityEngine.Object. Everything else is deserialized as values directly and never null.

    Question would then be though why have such a debug tool for this case instead of simply checking directly

    private void Start()
    {
        if(!someObject) Debug.LogError($"{nameof(someObject)} is not assigned", this);
    }
    

    advantage of this would be:

    • Double click on the log message and directly jump to according code line
    • Click once on the message highlights according object in the Hierarchy (or the assets)
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search