问题描述
我有一个特定的函数,它可以将 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
来自其中的大多数内置类型,特别是 GameObject
、ScriptableObject
和 继承.
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 == null
→ false
但是,解决方案可能是使用 bool
Object
的代码> 运算符.这取代了
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
一般:
只要这是检查器中任何
Component
的 public
或 [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
...
}
这篇关于当我转换为对象时,空游戏对象变为非空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!