这是一篇晦涩难懂的片面的研究
一,简单的继承层次
class CA { } class CB : CA{ } class CC : CB{ } } void Test(CA oa){//CATest Debug.Log ("CA==============="); } void Test(CB oa){//CBTest Debug.Log ("CB==============="); } void Test(CC oa){//CCTest Debug.Log ("CC==============="); } //测试代码如下: CC oc = new CC (); Test (oc);
在这种情形下调用Test(oc)有如下规律:<通过注释掉其它函数进行测试>
- 若CATest , CBTest, CCTest三个重载函数都存在,则Test(oc)将调用CCTest
- 若只有CATest, CBTest二个重载函数,则Test(oc)将调用CBTest
- 若三个函数只有一个存在,则Test(oc)即调用该函数。
由此我们得知,Test(oc)调用时,编译器会由oc的继承层次由子到父的优先级去匹配重载函数的形参。这也符合正常逻辑。
二,类中有运算符重载的继承
class CA { } class CB : CA{ } class CC : CB{ /* MSDN类型转换的要求 1.操作数必须是封闭类型 2.类A到类B的类型转换定义不能在类C中进行(即2个类的转换不能在第3个类中定义, 如下面的参数定义)*/ public static implicit operator bool(CC ot/*不能是CA ot或 CB ot */){ Debug.Log ("bool================"); return ot != null; } } void Test(CA oa){//CATest Debug.Log ("CA==============="); } void Test(CB oa){//CBTest Debug.Log ("CB==============="); } void Test(CC oa){//CCTest Debug.Log ("CC==============="); }
void Test(bool b){//boolTest
Debug.Log ("b===============")
}
//测试代码如下: CC oc = new CC (); Test (oc);
此情形下boolTest重载函数和CATest, CBTest, CCTest的任何一个重载都冲突,原因如下:
当调用Test(oc)时,编译系统将oc与Test的四个重载函数的参数进行匹配,却发现四个都能匹配成功。Test(bool b)通过CC类的bool类型符重载而匹配。
CATest, CBTest, CCTest三个重载函数由于形参CC,CB,CA是继承关系,在进行匹配时是有优先级的,由于ot是CC类型的,所以优先级CC>CB>CA,因此这个三个重载函数间没有冲突,编译器明确知道该调用哪个重载。而bool重载与CC,CB,CA在类型转换时是同优先级,因此编译系统不知道该调用bool重载还是CC,CB,CA的三个重载了。
若将bool重载由类CC移到类CA中,其它代码不变,测试代码不变。经测试,boolTest,CATest, CBTest, CCTest四个重载可以共存,即boolTest与其它任何一个重载都不冲突。
- boolTest,CATest, CBTest, CCTest同时存在,Test(oc)调用了CCTest
- boolTest,CATest, CBTest同时存在,Test(oc)调用了CBTest
- boolTest, CATest同时存在,Test(oc)调用了CATest
- boolTest,CATest, CBTest, CCTest只有一个存在,则调用此存在
- 只有boolTest存在时,该重载函数也被调用
这说明了基类的类型重载运算符的调用优先级低于父子层级转换的优先级,如情形5,在只有bool重载运算符时才会被调用。
本类的类型重载运算符的优先级等于父子层次转换的优先级。
三,Unity中的调用
UnityEngine所有类的的基类都是UnityEngine.Object。这个类与System.Object的关系很诡异。
System.Object obj = new UnityEngine.Object() //这行代码在编译上没问题,其实非常诡异,后面单独说
这样写可以正常编译。反过来将System.Object赋予UnityEngine.Object则不能编译通过。
这似乎可以说 System.Object 是 UnityEngine.Object的基类,网上许多人也这么认为。还有人说是隐式继承。
然而,通过看U3D的API,可以看到UnityEngine.Object并没有继承任何类,UnityEngine的源码中Object也确实没有继承任何类。这只能说是CLR内部自己的搞的鬼。
于是在这种情形下,重载函数的调用规律就有了一点小的改变。
这就是:UnityEngine.Object类及子类适用于上面的规律。而System.Object则处于所有类型的最低优先级,低于bool类型转换重载。例:
public class NewBehaviourScript : MonoBehaviour { void OTest(bool b){//F1 Debug.Log ("OTest--b-"); } void OTest(System.Object obj){//F2 Debug.Log ("OTest-system-obj-"); } void OTest(UnityEngine.Object obj)//F3 { Debug.Log ("OTest-obj"); } void OTest(Transform tran){//F4 Debug.Log ("OTest-trans-"); } void Start (){ OTest (gameObject.transform); } void Update () { } }
调用优先级F4>F3>F1>F2。即:
- F1,F2,F3,F4共存时,OTest (gameObject.transform)调用F4,因F4形参为Transfrom类型,与实参相同,接近度最高。
- 若仅有F1,F2,F3共存,OTest (gameObject.transform)调用F3,因F3形参为UnityEngine.Object类型,是实参类型Transfrom的直接父类,接近度最高。
- 若仅F1,F2共存,因System.Object只是UnityEngine.Object的隐式父类,在语法上已不是其父类了,这时编译器会试着寻找bool类型重载,结果找到了。
- 若仅F2存在,编译器既没找到可用的直接转换,也没找到bool重载,于是就剩下隐匿父类可以尝试了,于是调用F2。
当仅有F1,F2共存时,大多数人的直觉是:OTest (gameObject.transform)肯定会调用F2: OTest(System.Object obj),网上有些同学很早发现了这个诡异现象
附:System.Object obj = new UnityEngine.Object() 这个诡异问题。
- new 一个UnityEngine.Object的对象是不合语义的,可以看到Unity API中的描述:Instatiating a GameObject adds it to the scene so it's completely initialized (!destroyed). Instantiating a simple UnityEngine.Object has no such semantics, so the it stays in the 'destroyed' state which compares
true
tonull
- UnityEngine.Object obj = new UnityEngine.Object() //null 这行代码的结果是obj为null,如1中所述。然而
- System.Object obj = new UnityEngine.Object() //诡异在这里,调试数据如下
可以看到suo 与 uo的调试数据都是 {null},这大概就是表示对象为空吧,if(uo==null)成立,输出了 uo==null字符串,然而if(suo==null)却不成立!!对于神奇的.NET,我只能说:用unity时就别用system.Object了。