Search code examples
c#visual-studiounity-game-engine

Null check is not working as expected in C#


I have a simple null check one of the object, it is something like:

if (prevActivity != null) 
{
    // not null
}  
else 
{
    // null
}

I check this is in the update hook of mono behaviour, for some reason it always ends up in the else block, even if the object is clearly not null.

This is a screenshot of me stepping in using debug:

enter image description here

Am I doing something wrong? I thought it was getting mutated somehow, but I don't think that's the case as this is how I set it:

if (currentActivity != null && !currentActivity.Equals(prevActivity)) 
{
    prevActivity = new Activity(currentActivity);
}

This is what my activity class looks like :

public class Activity {
    public ActivityName activityName;
    public string label;
    public ActivityState state;
    public bool conditionsMetToPerform;
    // Note as of current design priority should be discreet for a particular unit
    public int weigth { get; private set; }
    public int energyCost { get; private set; }

    public Activity(ActivityName activityName, string label, int weigth) {
        this.activityName = activityName;
        this.label = label;
        this.weigth = weigth;
        this.state = ActivityState.NotStarted;
        this.conditionsMetToPerform = false;
    }

    public Activity(Activity activity) {
        this.activityName = activity.activityName;
        this.label = activity.label;
        this.weigth = activity.weigth;
        this.state = activity.state;
        this.conditionsMetToPerform = activity.conditionsMetToPerform;
        this.energyCost = activity.energyCost;
    }

    public void setEnergyCost(int energyCost) {
        this.energyCost = energyCost;
    }
    public enum ActivityName {
        Wander, BuyHealing, BuyArmor, BuyWeapon, Hunt, Flee, Fight, Berserk, RestAtHome, GoHome, Raid, Refuge, VisitBlacksmith
    }

    public enum ActivityState {
        NotStarted, InProgress, Completed, Abandoned
    }
}

Even if it was getting mutated not sure why the debug acts the way it does when stepping thru. Maybe something stupid I am overlooking here?


Solution

  • According to Unity's documentation Null references:

    Custom == operator

    For types that inherit from UnityEngine.Object, Unity uses a custom version of the C# equality and inequality operators. This means the null check in the previous example (myGameObject == null) can evaluate true (and conversely myGameObject != null can evaluate false) even if myGameObject technically holds a valid C# object reference. This happens in two cases:

    1. The object can be a so-called “fake null” or placeholder object which Unity uses in the Editor only to populate uninitialized MonoBehaviour fields. These objects store useful debugging information to help you locate the source of these fields if you try to reference them.

    2. The object can be a managed (C#) object which has not yet been garbage collected but which should be considered null because the unmanaged (C++) counterpart object has been destroyed.

    Your code is correct. You must treat the “fake null” objects and invalid objects like a real null (as @derHugopointed out).


    FYI (do not apply the following null-checks to objects deriving from UnityEngine.Object):

    In C#, the expression is null always performs a real (C#) null-check which does not use possibly overridden equality operators.

    object.ReferenceEquals(currentActivity , null) also works.

    E.g.

    if (!(currentActivity is null) && !currentActivity.Equals(prevActivity))
    

    You could also use

    if (!(currentActivity?.Equals(prevActivity) ?? true))
    

    (There is also a is not pattern that I think was introduced in C# 9.0, but Unity uses C# 8.0 as far as I know.)