C#和Unity的Object、Object之间的相等比较操作以及Object的空检查(null check)

1 篇文章 0 订阅
前些天遇到的一个问题:

在lua侧有个方法是用来检测gameobject是否为空的,代码如下:
在这里插入图片描述
这个方法在被某段代码调用时会偶发的报错,报错内容如下:
在这里插入图片描述
在某次报错时,从断点中看到如下信息:
在这里插入图片描述
从这里能看出:
1)首先这个object在lua看来不是空的,即不是nil
2)但是这个object又确实是空的,里面没有本该有的数据(而且ide也很贴心的提示了invalid C# object)

查了下xLua的FAQ,发现是unity对C# object的判空操作进行了重载,简而言之是这样的:当一个object为null时,unity出于让unity开发者使用起来更方便的考虑,在这个null object中加入许多辅助信息(比如堆栈信息之类的),此时虽然unity的判空操作认为这个object为空,但实际上并不是C#意义上的真正的空对象,此时lua就把这个伪空对象包装了一下传到lua侧使用。而Equals这个方法只会判断它的参数是不是空的,并不会判断自己所属的这个对象是不是空的,于是就出现了nullobject.Equals(param)的情况,就导致了报错。xLua也给出了解决方案:在这里插入图片描述

下面展开讲一下上面段落里标注的文本

首先我们要知道虽然Unity使用C#开发,但Unity本身是C/C++的,于是object就有两套,一套是C++的,一套是C#的。C++那套是存储了所有数据的,而C#那套是对C++ object进行了封装,里面存储的是指向C++的指针。C#中对Unity引擎代码的调用(比如object.SetActive())都会在C++侧被执行。
两套object有两套生命周期,一套由C#的托管堆控制,另一套则在C++中由代码控制。举个例子:当Destroy方法被调用来销毁一个物体时,这个物体在C++侧存储的数据会被销毁(实际上Destroy被调用时只是给object设置了一个销毁标记,真正的销毁操作要等到update之后render之前。所以想要知道一个object是否被销毁要在update之后进行判断),这个物体上的C#组件将被移除,但这些组件仍会被C#侧持有,直到C#触发gc销毁这些组件。所以就会出现上面那样的问题:当一个C++ object被销毁以后,又去访问这个object,此时由于C#中的object还没有被销毁Unity就返回了一个伪空对象。

一些展开内容

1.判断是否相同的几个方法
查上面的资料的时候,看到了一些介绍unity对比较是否相等的操作进行重载的内容,这里也大概记一下。先讲一下C#的三个方法:
1)ReferenceEquals

public static bool ReferenceEquals (object objA, object objB);

这个方法是object中的一个静态方法并且不可被重写,它需要两个object类型的参数,最后返回一个bool值表示两个参数是否拥有相同的内存地址
2)Equals

public virtual bool Equals (object obj);

Equals也是object中的方法,不同的是它是个虚方法,它对于引用类型会默认比较持有的引用是否相同,对于值类型会默认比较各个字段。它只接受一个参数,所以只会判断参数是否为空,而不会判断自身,如果自身为空的话会抛出NullReferenceException的异常。同时也要注意参数需要与自身类型一致。
3)==
== 默认用于内置的值类型比较,如果想要应用于别的类型则需要重载。注意重载时 ==!= 要成对存在。

这些方法在Unity中的表现:
unity在UnityEngine.Object中重载了 ==,所以继承自UnityEngine.Object的类型都会使用该方法。对于别的类型则会使用C#默认的 ==。由于unity在重载 == 的逻辑中添加了自定义的空检查逻辑:除了检查object是否真的为空以外,还会检查这个object是不是伪空对象,导致使用 == 进行比较运算时会比较耗时。而另外两种方法则和C#中的没有区别。

2.关于?.、?[]、??、??=
unity只是重载了 == ,并没有去重载这个操作符,所以尽管这些操作符也有空检查但却是C#的意义上的,是不走unity自定义的空检查的(别的资料大多表述为这些操作符会跳过unity的生命周期检查),所以使用这些操作符可能会和使用 == 得出不同的结果。

【一些参考资料】
【1】xLua下调用GetComponent时返回值不是nil的坑
【2】xLua无法判断Unity对象为nil的问题
【3】Unity’s Scripting Duality and Object Destruction
【4】Custom == operator, should we keep it?
【5】Unity-自定义==运算符,我们应该保留它吗 (【4】的翻译)
【6】Unity-UnityEngine.Object的自定义比较
【7】Possible unintended bypass of lifetime check of underlying Unity engine object
【8】Unity-Resharper-可能意外绕过Unity引擎对象的底层生命周期检查(【7】的翻译)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值