I'm confused about the return value of GetComponent
if the requested component is not attached to the object. According to the Unity documentation, GetComponent
should return null. However, what appears to be happening is that GetComponent
is returning a "null" object of the requested type, rather than the value null
.
In this example, my game object does not have a CircleCollider2D
attached to it. When I set a breakpoint on the line CircleCollider2D x = GetComponent<CircleCollider2D>();
, I get this result
Why is the returned value not null
?
EDIT:
Here's a full screenshot of the code and the values in the debugger.
ANOTHER EDIT:
Could it be that Unity has overloaded the ==
operator so that GetComponent
always returns a object, but the object can have an internal "null" state which returns true when compared to null
? I can see the following declarations in the UnityEngine
namespace
public static bool operator ==(Object x, Object y);
public static bool operator !=(Object x, Object y);
It seems like GetComponent<T>()
doesnt return TRUE null
. Instead it returns new T with default values that fires MissingComponentException
when using any null field. GetInstanceID()
and GetHashCode()
work because they only use int m_InstanceID
which is set to default 0. Not sure how ToString()
works but it probably returns "null"
when m_InstanceID == 0
.
Proof:
void Start()
{
CircleCollider2D getComponent = GetComponent<CircleCollider2D>();
CircleCollider2D empty = null;
CircleCollider2D newCC = new CircleCollider2D();
Debug.LogFormat("getComponent.GetInstanceID() {0}", getComponent.GetInstanceID());
Debug.LogFormat("newCC.GetInstanceID() {0}", newCC.GetInstanceID());
try
{
Debug.LogFormat("empty.GetInstanceID() {0}", empty.GetInstanceID());
}
catch (System.NullReferenceException e)
{
Debug.Log("empty.GetInstanceID() doesnt work, im true null");
}
try
{
string t = getComponent.name;
}
catch (System.Exception e)
{
Debug.Log(string.Format("getComponent fires {0} when any field is null",
e.ToString()));
}
try
{
string t = newCC.name;
}
catch (System.Exception e)
{
Debug.Log(string.Format("newCC fires {0} when any field is null",
e.ToString()));
}
}
Results:
getComponent.GetInstanceID() 0
newCC.GetInstanceID() 0
empty.GetInstanceID() doesnt work, im true null
getComponent fires UnityEngine.MissingComponentException
newCC fires System.NullReferenceException
Also:
getComponent.GetHashCode() = 0
getComponent.ToString() = "null"
Why getComponent == null
is true? Usually it's just:
`getComponent.GetInstanceID() == otherComponent.GetInstanceID()`
In o == null
case it's:
return !(
o.GetCachedPtr() != IntPtr.Zero || (!(o is MonoBehaviour) &&
!(o is ScriptableObject) &&
Object.DoesObjectWithInstanceIDExist(o.GetInstanceID())));
So i guess object with InstanceID = 0 never exists. Search for decompiled UnityEngine/UnityEngine/Object.cs if u want to know more.
Could it be that Unity has overloaded the == operator
Tldr: Yes .
Why is the returned value not null?
According Custom == operator, should we keep it? :
When a MonoBehaviour has fields, in the editor only, we do not set those fields to “real null”, but to a “fake null” object.
Why? According that article, basically for two reasons:
On Editor mode: get more contextual information on invocations, so if you have an exception you will be able to know which object was the cause.
Every that comes from UnityEngine.Object
are wrapped in a C# object to keep the reference to the Unity C/C++ GameEngine Object. In the editor/code we work with the C# elements but those are just a reference to the 'real' c++ objects. Now, the lifetime of those 'real' c++ objects are handled explicitly (if you destroy
something the C++ object it will be destroyed and the allocated memory it will be freed) but the C# object will be completely destroyed when the Garbage Collector decides to free that memory. So you will have a not null C# object pointing to a null C++ object. With this fake null trick you will know that this specific C# object is null
because in the C++ side it was destroyed.
This is also important when if you have a singleton and if (sm_Instance == null)
is not working as expected, you can use: if (EqualityComparer<T>.Default.Equals(sm_Instance, null))
instad.
Edit: Probably this can be caused if your singleton use generics and your type constrain is class
public class Singleton<T> where T : class {
This will cause to use the ==
operator of a class object, even when your Singleton is a GameEngine Object. One fix for that? Instead of class
you can use UnityEngine.Object
, like:
public class Singleton<T> where T : UnityEngine.Object {
Related Thread:
https://forum.unity3d.com/threads/null-check-inconsistency-c.220649/#post-1775701
Ps: If you go to UnityEngine.Object assembly, you will be able to see the overload:
Almost at the end...
if you are still looking for solution this worked for me`
var component = parent.GetChild(i).GetComponent<T>();
if(component != null)
{
if (component.ToString().Equals("null") == false)
{
list.Add(component);
}
}
GetComponent
is expected to return object of type 'Component . Hence it is not expected to return "null" rather ~null
. Hence it is not expected to return "null" rather ~null
is expected value. Perhaps this strange behavior should be reported to unity3d
,
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.