本文介绍了当我转换为对象时,空游戏对象变为非空的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个特定的函数,它可以将 object 作为参数.因此它可以是 MonoBehaviour 或 GameObject:

I have a particular function which can take an object as an argument. This is so it can be either a MonoBehaviour or a GameObject:

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

The wierd thing is, when I convert a null GameObject to an Object, it no longer appears as 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 调用永远不会在这里运行,因为对象不为空.

The Log call never gets run here, because the object is not null.

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

If I put Debug.Log(offHandEquipLocation == null) in the Start() function, it prints true. If I put Debug.Log(field == null) in the RequireField() method, it prints false.

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

Is there a way I can see if the object is null?

推荐答案

关于这个的第一个想法:不要这样做^^

A very first thought in general on this: Do not do it ^^

这有一个问题:您更改了调用的堆栈跟踪.通过在控制台中看到错误时将空检查传递给静态类,您之前无法直接查看它是从哪里抛出/记录的,通过双击它直接转到相应的代码行并且不能(那么简单) 在层次结构中突出显示相应的对象上下文".

There is one issue with this: You change the stacktrace of the call. By passing the null check to a static class when you see the eror in the console you can not like before directly see where it was thrown/logged from, directly go to the according code line by double clicking it and can't (that easy) highlight the according object "context" in the hierarchy.

所以我总是尽可能地将检查结果与实际使用情况保持一致,而不是像这样去做

So I would always keep the checks as close to the actual usage as possible and rather do it like

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

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

The whole sense of Debug.Log is making your life easier when debugging ;) Using the above approach offers way more advantages than the fact that using your approach you don't have to type Debug.LogError($"{nameof(offHandEquipLocation)} is not referenced!", this); multiple times.

正如其他人和我已经提到的,它不能像您预期的那样工作的原因是 Unity 的 自定义 == null 类型的行为 Object 来自其中的大多数内置类型,特别是 GameObjectScriptableObject 和 继承.

As others and I already mentioned the reason why it doesn't work as you expect is Unity's custom == null behavior for the type Object from which most built-in types in particular GameObject, ScriptableObject and Component inherit.

即使 Object 可能看起来be"或者更好的说法是返回一个值等于的值null Unity实际上仍然在引用中存储一些元信息以抛出自定义异常(MissingReferenceException, MissingComponentException, UnassignedReferenceException) 比一个简单的 NullReferenceException(你也可以看到 此处).因此,它实际上不是底层 object 类型中的 null.

Even though an Object "might appear to be" or better said returns a value equal to the value of null Unity actually still stores some meta information in the reference in order to throw custom exceptions (MissingReferenceException, MissingComponentException, UnassignedReferenceException) which are more self explanatory then a simple NullReferenceException (as you can also see here). It therefore actually is not null in the underlying object type.

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

As soon as you convert it to object the custom == null behavior is gone but the underlying object still exists and therefoe you get field == nullfalse

但是,解决方案可能是使用 boolObject 的代码> 运算符.这取代了 null 检查,意味着类似于 此对象已被引用、存在且尚未销毁..并继续使用 == null 做其他事情

However a solution might be creating a simple overload using the bool operator for Object. This replaces the null check and means something like This object is referenced, exists, and was not destroyed yet.. And keep using == null for anything else

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 传递它,以使其更安全地重命名

Now you can use both. As a little note: I wouldn't use a verbal string for the first parameter but instead always pass it inusing nameof to make it more secure for renamings

var GameObject someObject;
var string someString;

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

关于您使用它的论点的一点旁注,例如string 一般:

只要这是检查器中任何 Componentpublic[SerializedField] 字段(MonoBehaviour>) 或 ScriptableObject 它总是由 Unity Inspector 本身使用默认值初始化.因此,对任何一个空检查,例如

As soon as this is a public or [SerializedField] field in the Inspector for any Component (MonoBehaviour) or ScriptableObject it is always initialized by the Unity Inspector itself with a default value. Therefore a null check for any of e.g.

public int intValue;
[SerializeField] private string stringValue;
public Vector3 someVector;
public int[] intArray;
...

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

is redundant since none of them will ever be null. This counts for any serializable type as well so even if you have a custom class

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

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

and then use a serialized field in your behaviour like e.g.

public List<Example> example;

而且这个永远不会是null.

顺便说一句,前面也提到过 Validation 不应该从 MonoBehaviour 继承,而应该是

Btw as also mentioned already Validation should not inherit from MonoBehaviour but rather be

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

这篇关于当我转换为对象时,空游戏对象变为非空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-17 03:07
查看更多