GetComponent returns null however comparison to null returns false
Look the result of the following function
public static T FindComponent<T>(this GameObject obj) where T : class
{
T a = obj.GetComponent<T>(); // Shows as "null"
T b = obj.GetComponentInChildren<T>(); // Some correct object
T c = a ?? b; // c is set to a
bool d = a == null; // false
bool g = a != null; // true
bool f = a.ToString() == "null"; // true
return obj.GetComponent<T>()
?? obj.GetComponentInChildren<T>()
?? obj.GetComponentInParent<T>();
}
a
is essentially null but a == null
returns false. Nevertheless debugger shows a
to be null. If a code uses this function and attempts do use that component this exception is thrown:
MissingComponentException: There is no 'T' attached to the "[ObjectName]" game object, but a script is trying to access it. T
being the component type passed to the function. I am clueless as to how this is even happening. Interesting thing is a.ToString() == "null"
is true
Answer by Bunny83 · Sep 14, 2016 at 06:14 PM
As NoseKills said the problem is that Unity has implemented a custom "==" operator for UnityEngine.Object. For all types which are derived from UnityEngine.Object the compiler will use that overloaded operator. However the operator selection is not dynamic. So the right operator is choosen at compile time.
Since you use a generic method here the actual type of "T" is not known at compile time. Your only constraint is "class" which restricts T to by a reference type. So the == operator used is the normal System.Object == operator and not Unity's == operator.
Try this:
bool d = (UnityEngine.Object)a == null;
It should return true for fake null objects.
You can't use the "null coalescing operator" ( ?? ) since it always uses the System.Object "==" operator.
So to get to a solution you should get rid of the "??" operators.
So one solution would be to change your generic constraint to "Component" instead of "class". This will make all "T" variables of type Component and ensures that "==" works.
However if you want to be able to pass interfaces as types, you either have to cast the result to UnityEngine.Object (or Component) and then do the check, or use "Equals"
btw: "GetComponentInChildren" will also do a GetComponent. The name is misleading but GetComponentInChildren will first look on the gameobject itself and then inside children.
public static T FindComponent<T>(this GameObject obj) where T : class
{
T res = obj.GetComponentInChildren<T>();
if (res != null && !res.Equals(null))
return res;
return obj.GetComponentInParent<T>();
}
This should do what you want. The Equals check will tell you about "fake null" objects. Though you can only use Equals when you know the reference isn't actually null ^^. Equals is a virtual instance method, so it is dynamically dispatched.
Very good explanation, I had no idea the ==
replacement is deter$$anonymous$$ed at compile time. I followed the second path. Also I wish GetComponentInChildren
was better named. Btw you mentioned passing interfaces. If I have component DoesX : $$anonymous$$onobehaviour, IDoesSomething
and do GetComponent<IDoesSomething>()
on the object that has the component attached, will it return the correct object? I thought GetComponent
is limited to concrete types
In the past unity had a constraint on the generic parameter of all GetComponent methods. So it only allowed types derived from Component. However they removed it somewhere between unity4.5 and unity5.x
So yes, you can now directly use interfaces with GetComponent.
From a pure software design point of view the constraint was a logical thing. However since the generic version actually uses the System.Type version under the hood, the constraint had no benefit's. It just restricted the usage. Of course without the constraint you could also pass types which clearly aren't components, but that's also possible with the non generic version.
Answer by NoseKills · Sep 14, 2016 at 04:43 PM
The fact alone that you get a MissingComponentException and not a NullReferenceException gives away a lot. In C#, if an object truly is null and you reference its members, you will get a genuine NullReferenceException. There's no way around it.
Unity does funky things when destroying or e.g GameObjectFind()ing non-existent UnityEngine.Object instances and that causes certain means of checking for null to return unexpected results. "??" is one of them.
Unity, at least inside the Unity editor, will return fake null objects on purpose to actually create the "$$anonymous$$issingComponentException". So GetComponent will actually always return a value when you test inside the editor. Though when no component is found a fake null object is returned which, usually, is detected as null unless the reference get casted to System.Object for some reason.
Note: When you run a built, Unity doesn't create fake null objects on purpose anymore, however you can still come across these when an object is destroyed. References to UnityEngine.Objects pretend that they are null once you destroyed them.