c# - 当我转换为对象时,null GameObject 变为非空

标签 c# unity3d

我有一个特殊的函数,它可以将一个对象作为参数。这样它就可以是 MonoBehaviour 或 GameObject:

奇怪的是,当我将 null GameObject 转换为 Object 时,它不再显示为 null:

public class EquipmentSlots : MonoBehaviour {

  // This is null: it hasn't been set in the inspector
  public GameObject offHandEquipLocation;

  void Start () {
    Validation.RequireField(
      "offHandEquipLocation",
      offHandEquipLocation
    ) 
  }

}

public class Validation : MonoBehaviour {
  public static void RequireField(string name, object field) {
    if (field == null) {
      Debug.Log($"{name} field is unset");
    }
  }
}

Log 调用永远不会在这里运行,因为该对象不为空。

如果我将 Debug.Log(offHandEquipLocation == null) 放在 Start() 函数中,它会打印 true。如果我将 Debug.Log(field == null) 放在 RequireField() 方法中,它会打印 false

有什么方法可以查看对象是否为空?

最佳答案

一般来说,第一个想法是:不要这样做 ^^

这有一个问题:您更改了调用的堆栈跟踪。当您在控制台中看到错误时,通过将 null 检查传递给静态类,您以前无法像以前那样直接看到它从哪里抛出/记录,通过双击它直接转到相应的代码行,但不能(这很容易) 在层次结构中突出显示相应的对象“上下文”。

所以我总是会尽可能地让检查接近实际使用情况,而不是像这样做

if(!offHandEquipLocation) Debug.LogError($"{nameof(offHandEquipLocation)} is not referenced!", this);

Debug.Log 的整体意义是让您在调试时更轻松 ;) 使用上述方法比使用您的方法不必键入 Debug.LogError($"{nameof(offHandEquipLocation)} 未被引用!", this); 多次。


正如其他人和我已经提到的,它无法按预期工作的原因是 Unity 的 custom == null 类型的 Object 行为,大多数内置类型特别是 GameObjectScriptableObject Component 继承。

即使 Object“可能看起来是”或更好地说返回一个值等于 null 的值,Unity 实际上仍然在引用中存储一些元信息为了抛出自定义异常(MissingReferenceExceptionMissingComponentExceptionUnassignedReferenceException),这些异常比简单的NullReferenceException(你也可以看到 here )。因此,它在底层 object 类型中实际上不是 null

一旦将其转换为 object,自定义 == null 行为就消失了,但底层的 object 仍然存在,因此您得到字段 == nullfalse


然而,一个解决方案可能是使用 bool operatorObject 创建一个简单的重载。这取代了 null 检查,意味着类似于 This object is referenced, exists, and was not destroyed yet.。并继续使用 == null 做其他事情

public static void RequireField(string name, object field) 
{
    if (field == null) Debug.LogError($"{name} field is unset");
}

public static void RequireField(string name, Object field) 
{
    if (!field) Debug.LogError($"{name} field is unset");
}

现在您可以同时使用两者。需要注意的是:我不会使用口头的 string 作为第一个参数,而是总是使用 nameof 传递它,以使其更安全地重命名

var GameObject someObject;
var string someString;

Validation.validate(nameof(someObject), someObject);
Validation.validate(nameof(someString), someString);

关于您在例如字符串一般:

一旦这是检查器中任何组件public[SerializedField]字段(MonoBehaviour) 或 ScriptableObject 它总是由 Unity Inspector 本身使用默认值初始化。因此,对任何例如

进行空检查
public int intValue;
[SerializeField] private string stringValue;
public Vector3 someVector;
public int[] intArray;
...

是多余的,因为它们都不会是 null。这也适用于任何可序列化类型,因此即使您有自定义类也是如此

[Serializable]
public class Example
{
    public string aField;
}

然后在您的行为中使用序列化字段,例如

public List<Example> example;

而且这个永远不会是null


顺便说一句,正如已经提到的那样,Validation 不应该继承自 MonoBehaviour 而应该是

public static class Validation
{
    // only static members
    ...
}

关于c# - 当我转换为对象时,null GameObject 变为非空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58068696/

相关文章:

c# - 具有泛型的隐式运算符不适用于接口(interface)

unity3d - 如何在 Unity3d 中为多个行星制作重力

c# - 使用 C# 的 Google 语音识别 REST API 的错误请求错误

c# - 统一: Rotate gizmo with an offset of 45

c# - Unity Android task kill 没有触发 OnApplicationQuit 和 OnDestroy

c# - 如何将xml字符串UTF8转换为UTF16?

c# - asp.net c#中如何对字符串进行加密

c# - Unity3D - 一种将一个字段的值与另一个字段或值进行比较的通用方法

c# - WPF 的 OpenDialog

c# - 将数据从 Controller 发送到 View 的最安全、最干净的方法是什么?