获取组件时返回“null”而不是null

3
我对于当请求的组件未附加到对象时,GetComponent 的返回值感到困惑。根据Unity文档,GetComponent 应该返回null值。然而,实际发生的情况是GetComponent返回请求类型的“null”对象,而非值null
在这个例子中,我的游戏对象没有附加CircleCollider2D。当我在行CircleCollider2D x = GetComponent<CircleCollider2D>();上设置断点时,我得到这个结果
为什么返回值不是null
编辑: 这里是代码和调试器中的值的完整截图。 另一个编辑:
Unity是否已经重载了==运算符,使得GetComponent总是返回一个对象,但该对象可以具有内部的“null”状态,并且与null进行比较时返回true?我可以在UnityEngine命名空间中看到以下声明。
public static bool operator ==(Object x, Object y);
public static bool operator !=(Object x, Object y);

然而,看起来发生的情况是GetComponent返回了一个请求类型的“null”对象,而不是值为null。你是怎么得出这个结论的?请展示你的代码——没有看到代码就很难给你好的建议。 - mjwills
代码只是 CircleCollider2D x = GetComponent<CircleCollider2D>(); 我把它放在我的 Awake 方法里。我在那一行上打了一个断点,截图是在跨过那一行后从本地窗口中获取的。 - Ben Rubin
x.GetType() 返回什么?而且,说真的,随着你在问题中添加更多的代码,这个过程会变得更加容易。 - mjwills
1
CircleCollider2D 是一个结构体还是一个类?你能展示一下它的源代码吗(也许可以通过 https://www.jetbrains.com/decompiler/ 进行反编译)?http://answers.unity3d.com/questions/1243356/getcomponent-returns-null-however-comparison-to-nu.html 有帮助吗? - mjwills
1
是的,Unity论坛上的那个答案确实有帮助。我很高兴看到这是预期的行为,而我并没有疯掉。谢谢。 - Ben Rubin
显示剩余2条评论
4个回答

6
似乎GetComponent<T>()不会返回TRUEnull。相反,它会返回具有默认值的新T,当使用任何空字段时会触发MissingComponentExceptionGetInstanceID()GetHashCode()有效,因为它们仅使用设置为默认值0的int m_InstanceID。不确定ToString()如何工作,但它可能在m_InstanceID == 0时返回"null"

证明:

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()));
        }
    }

结果:

getComponent.GetInstanceID() 0
newCC.GetInstanceID() 0
empty.GetInstanceID() doesnt work, im true null
getComponent fires UnityEngine.MissingComponentException
newCC fires System.NullReferenceException

此外:
getComponent.GetHashCode() = 0
getComponent.ToString() = "null"

为什么getComponent == null是真的?通常是这样的:
`getComponent.GetInstanceID() == otherComponent.GetInstanceID()`

o == null的情况下,它是这样的:
return !(
o.GetCachedPtr() != IntPtr.Zero || (!(o is MonoBehaviour) && 
!(o is ScriptableObject) &&
Object.DoesObjectWithInstanceIDExist(o.GetInstanceID())));

所以我猜 InstanceID = 0 的对象从未存在过。 如果想了解更多信息,请搜索反编译的 UnityEngine/UnityEngine/Object.cs。

实际上,default(IntPtr) = IntPtr.Zero,因此 o.GetCachedPtr() == IntPtr.Zero - kamyker

2
可能是Unity重载了==运算符。
简而言之:是的。
为什么返回值不是null?
根据这篇文章“Custom == operator, should we keep it?”:
当MonoBehaviour有字段时,在编辑器中,我们不将这些字段设置为“真正的null”,而是设置为一个“假null”对象。
为什么?根据该文章,基本上有两个原因:
1.在编辑器模式下:获取更多调用的上下文信息,因此如果出现异常,您将能够知道哪个对象是原因。
2.来自UnityEngine.Object的所有内容都包装在C#对象中,以保留对Unity C/C++ GameEngine对象的引用。在编辑器/代码中,我们使用C#元素,但这些元素只是指向“真实”c++对象的引用。现在,这些“真实”c++对象的生命周期是显式处理的(如果您销毁某些东西,则C++对象将被销毁并释放分配的内存),但是当垃圾收集器决定释放该内存时,C#对象将完全被销毁。因此,您将具有指向空C++对象的非空C#对象。通过这种“假null”技巧,您将知道此特定C#对象为空,因为在C++方面它已被销毁。
这也很重要,因为如果您有一个单例,并且if(sm_Instance == null)不能按预期工作,则可以使用:if(EqualityComparer.Default.Equals(sm_Instance, null))来代替。
编辑:如果您的单例使用泛型并且类型约束为class,则可能会导致此问题。
public class Singleton<T> where T : class {

这将导致使用类对象的==运算符,即使您的Singleton是GameEngine对象。 一个解决方法?您可以使用UnityEngine.Object而不是class
public class Singleton<T> where T : UnityEngine.Object {

相关主题:
https://forum.unity3d.com/threads/null-check-inconsistency-c.220649/#post-1775701


附注:如果您进入UnityEngine.Object程序集,您将能够看到重载:

UnityEngine.Object

几乎在最后...

Overload


0

如果你仍在寻找解决方案,这个方法对我有效。

var component = parent.GetChild(i).GetComponent<T>();
if(component != null)
{
    if (component.ToString().Equals("null") == false)
    {
        list.Add(component);
    }
}

1
只需使用 if (component != null) { list.Add(component); } - Ruzihm

-1

GetComponent 应该返回类型为 'Component' 的对象。因此,不应该返回 "null",而是期望返回 ~null。也许这种奇怪的行为应该报告给 unity3d


1
我怀疑这个“奇怪的行为”实际上是有意为之的。 - Draco18s no longer trusts SE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接