Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A value is null, but the check returns otherwise?

EDIT: This explains everything! - Unity creates managed fake wrappers around your UnityEngine.Objects when you destroy them. That means if you destroy a UEObject, the C# wrapper could still be not null. The == is implemented in a custom way so that when you destroy a UEObject checking == null will return true. This obviously doesn't play well with generics.


This is literally driving me insane. I have this method right here:

public static void AssertNotNullAfterAssignment<T>(ref T value, Func<T> get, string msg) where T : class
{
    if (value == null)
        value = get();
    if (value == null)
        throw new NullReferenceException(msg);
}

I'm calling it on a reference that is null at the start:

AssertNotNullAfterAssignment(ref fovMeshFilter, GetComponent<MeshFilter>, "fovMeshFilter");

What's really insane, is that the check if (value == null) is returning false! even though the value IS null!

Here's a short video I made showing this.

What's interesting though, if I copy/paste the method code to the method I used it in (in OnEnable) it works (pretty much what I have commented in OnEnable in the video)

So... it works if it was outside the assertion method, but not inside. I also tried Equals instead of == but the same.

Anybody has any idea what's going on?

Edit: Here's OnEnable, the method I'm calling the assertion from:

private void OnEnable()
{
    //if (fovMeshFilter== null)
    //  fovMeshFilter= GetComponent<MeshFilter>();
    //if (fovMeshFilter== null)
    //  throw new NullReferenceException("fovMeshFilter");

    AssertNotNullAfterAssignment(ref fovMeshFilter, GetComponent<MeshFilter>, "fovMeshFilter");
}

If I use the uncommented lines it works as expected, but the assertion method doesn't work for some only-god-knows-why reason.

Edit1: What we have here?

enter image description here

Edit2:

enter image description here

Edit3:

So after the great help from this awesome community, and couple of tests I came to the solution. I could have sworn it was one of the very first things I tried! you must believe me! XD - It was just to use .Equals instead of == - Like @Edin shows in his answer, doing a == on a generic object seem to call System.Object's == - But, calling .Equals should always resolve to the correct overload of Equals. I don't know why is it this way, I mean, why don't the '==' also resolve to the correct overload?

like image 801
vexe Avatar asked Mar 20 '23 10:03

vexe


1 Answers

Your object is not null. Seeing null in debugger does not mean it is a null reference. If you can expand object in the debugger, it is most certainly not null reference. It might be that the DebuggerDisplay string or ToString() method return "null" in your case but that is not the same as null reference.

Take an example of this class

[DebuggerDisplay("null")]
class A { }

An instance a of A

var a = new A();

will be displayed as a|null in the debugger when you run over it with your mouse.

Your T most certainly has this attribute. ToString() override which returns "null" would add additional curly braces around the string: a|{null}.

EDIT:

Seeing your Edit2, made me realize what your real problem might be. Your == operator is most probably overriden, so that it returns true in some cases when you compare an instance of your class to null, although an instance itself is not null. However, in generic method it is not known what type your parameter is at compile time, therefore the most general operator will be applied on your T parameters, which is reference comparison. This is well described in the following thread: C# generics class operators not working

That could be the reason why the comparioson in OnEnable() works, and the comparison inside your generic method does not.

However this is not completely proven, since I cannot see your code. But you could verify that.

And here is a full working example of the operators not working properly in combination with generics:

class Person
{
  public string Name { get; set; }            

  public static bool operator ==(Person left, Person right)
  {
    // we consider Person null when it either has no Name or it is a null reference.
    if (object.ReferenceEquals(null, left) || left.Name == null)
      return object.ReferenceEquals(null, right);
    return left.Equals(right);
  }

  public static bool operator !=(Person left, Person right) { return !(left == right); }
  // Note that == and != operators should ideally be implemented in combination of Equals() override.
  // This is only for making an example for this question
}

private static bool IsNull<T>(T val)
{
  return val == null;
}

static void Main(string[] args)
{
  Person person = new Person();
  //this will display: person == null => True
  Console.WriteLine("person == null => {0}", person == null);
  //this will display: IsNull<Person>(person)=> False
  Console.WriteLine("IsNull<Person>(person)=> {0}", IsNull(person));
}
like image 126
Edin Avatar answered Mar 22 '23 01:03

Edin