UnityEngine.Object和System.Object
由编辑器引发的思考,普通类是UnityEngine.Object?
起因是我想在Unity的Inspector界面绘制一个普通类(类似每个Mono脚本的Script字段),一个普通类应该默认继承的是System.Object
,而使用EditorGUILayout.ObjectField绘制的是一个UnityEngine.Object,该如何绘制呢?
我写了个测试代码如下:
1 | public class NewClass |
1 | public class NewMonoBehaviour2 : MonoBehaviour |
此时我将新建的普通类拖上去居然能拖入!!!那么自己建的普通类属于UnityEngine.Object吗???
然后我又答应了一下这个obj
所在的父类
1 | public class NewMonoBehaviour2 : MonoBehaviour |
打印结果是:继承 form UnityEngine.TextAsset
->继承 form UnityEngine.Object
->继承 form System.Object
此时我怀疑的信了普通类也是继承了UnityEngine.Object,问了一下AI,都说普通类如果没有强声明继承System.Object,那就是隐氏继承了UnityEngine.Object
疑惑了一会我改了代码
1 | public class NewMonoBehaviour2 : MonoBehaviour |
此时打印的只有一个,就是继承 form System.Object
查阅了资料最后得出结论
- 不管是普通类还是继承了Mono的类,只要是C#文件,在
编辑器中
都会当成MonoScript文件来对待,把它当成Unity中可识别的资源(MonoScript - Unity 脚本 API),这里就解释了为什么一开始能拖到面板上,并且打印的是继承TextAssets,UnityObject… - 当在运行时,声明一个普通类需要通过
new
来声明,这个时候它是来自于.net内存中(先这么叫),所以就是继承System.Object
UnityEngine.Object和?. ??
首先微软没有提供?? ?.
这两的重写,所以UnityEngine.Object不支持,官方文档 Object - Unity 脚本 API
那么在如下代码为什么发现又是可行的呢?
1 | public class NewMonoBehaviour2 : MonoBehaviour |
从结果看,大可以这么认为,自己的脚本继承Mono属于C#层,可以使用??,但是BoxCollider2D是Unity引擎的C++层,所以不适用??, 再加上雨松的一篇文章:Unity3D研究院之UnityEngine.Object和System.Object,就更确信无疑了。
但是我们知道继承的Mono也是来继承自UnityEngine.Object,官方说了不支持?? ?. 这里发现是支持的,还为自己找了借口,难道错了吗?
查看Unity 中的 .NET 概述 - Unity 手册中有说到
Unity C# 和 Unity C++ 共享 UnityEngine 对象
使用诸如 Object.Destroy 或 Object.DestroyImmediate 等方法销毁 UnityEngine.Object 派生对象时,Unity 会销毁(卸载)原生对应对象。无法使用显式调用销毁 C# 对象,因为垃圾回收器会管理内存。一旦不再引用托管对象,垃圾回收器便会收集并销毁它。
如果再次访问已销毁的 `UnityEngine.Object,则 Unity 会为大多数类型重新创建原生对应对象。此重新创建行为的两个例外是 MonoBehaviour 和 ScriptableObject:一旦被销毁,Unity 便绝不会重新加载它们。
MonoBehaviour 和 ScriptableObject 会覆盖相等 (
==
) 和不相等 (!=
) 运算符。因此,如果将销毁的 MonoBehaviour 或 ScriptableObject 与 null 进行比较,则当托管对象仍然存在且尚未进行垃圾收集时,运算符会返回 true。因为
??
和?.
运算符不可重载,所以它们与从UnityEngine.Object
派生的对象不兼容。在托管对象仍然存在的情况下对销毁的 MonoBehaviour 或 ScriptableObject 进行使用时,这些运算符不会返回与相等和不相等运算符相同的结果。
主要最后一句:因为
??和
?. 运算符不可重载,所以它们与从 UnityEngine.Object 派生的对象不兼容。在托管对象仍然存在的情况下对销毁的 MonoBehaviour 或 ScriptableObject 进行使用时,这些运算符不会返回与相等和不相等运算符相同的结果。
意思是如果对销毁的Mono进行?? ?.
操作,不会返回正确的结果,代码如下
1 | public class NewMonoBehaviour2 : MonoBehaviour |
从代码结果来看,销毁了之后通过??就是不对的结果。这里的销毁时Unity引擎对Mono C++上的销毁,但是C#托管代码还在(UnityEngine.Object重写了== !=,可以通过System.Object.ReferenceEquals(go, null)
来判断是否还存在),此时用??判断结果是不为null,所以获取不到。
最后建议
- 对于继承
MonoBehaviour或者ScriptableObject
+Unity内置类都不要使用?? ?. 只对自己使用的普通类使用