.NET對象的幾種判等,耦合在一起,我盡量在任意一種判等裡不涉及其他判等方式。但是Instace.Equals仍然不法避免不使用到Operator ==,所以我把Operator ==作為第三部分內容。
三、Operator==深入本質
預設情況下,Operator==在對參考型別對象判等的本質和Object.ReferenceEquals是相同的即確定指定的變數是否是相同的執行個體。
當然C#.NET可對Opeartor==進行定義,鄙人認為應該避免自訂Operator==。任意定義將會對使用者對Opeartor==的作用產生混淆。畢竟沒有任何邏輯比a=a更有邏輯了。
對於自描述實值型別來說如果想使用Operator==進行判等,那麼就要對Opeartor==以及Opeartor!=分別進行定義。FCL的大部分實值型別都是這樣做的比如DateTime、Decimal等。
存在這樣的情況Int32、Int64、Byte等實值型別並未定義Opeartor==,但是編譯器仍然支援以上類型變數直接使用Operator==進行判等。同查閱相關資料得知包括byte、shrot、 int、long、char、float、double、bool、decimal、string、object、dynamic共十二種類型為基元類型。編譯器直接支援的資料類型稱為基元類型。基元類型直接映射到Framework類庫(FCL)中存在的類型。比如在C#中,int直接映射到System.Int32類型。
當兩個int類型變數進行判等,首先將兩個值壓入計算堆棧,CLR通過CEQ指令直接對值進行判等。當然基元類型是支援定義Opeartor的比如Decimal就定義了Opeartor==。
有趣的是,sbyte、ushort、uint、ulong並非基元類型,也未定義Opeartor,但是以上幾種類型直接使用Opeartor==判等,CSC編譯器仍然能夠編譯通過。以ushort為例,請看一下代碼:
1 static void Main()2 { 3 ushort u1=1;4 ushort u2 = 2;5 var b = u1 == u2;6 }
通過ILDASM反編譯,得到的IL代碼
1 .method private hidebysig static void Main() cil managed 2 { 3 .entrypoint 4 // 代碼大小 11 (0xb) 5 .maxstack 2 6 .locals init (uint16 V_0, 7 uint16 V_1, 8 bool V_2) 9 IL_0000: nop10 IL_0001: ldc.i4.1 //將整數值 1 作為 int32 推送到計算堆棧上。 12 IL_0002: stloc.0 //從計算堆棧的頂部彈出當前值並將其儲存到索引 0處的局部變數列表中。 13 IL_0003: ldc.i4.2 //將整數值 2 作為 int32 推送到計算堆棧上。15 IL_0004: stloc.1 //同上,儲存到索引1處的局部變數列表16 IL_0005: ldloc.0 //將索引 0 處的局部變數載入到計算堆棧上。17 IL_0006: ldloc.1 //同上18 IL_0007: ceq //如果這兩個值相等,則將整數值 1 (int32) 推送到計算堆棧上;否則,將 0 (int32) 推送到計算堆棧上。19 IL_0009: stloc.2 //同上20 IL_000a: ret21 } // end of method Program::Main
代碼中添加了一部分注釋,大概的意思就是將ushort隱藏的看作int類型,在通過CEQ指令對基元類型進行判等,類似於以下代碼:
1 static void Main()2 { 3 int i1 = 1;4 int i2 = 2;5 var b = i1 == i2;6 }
關於Instance.Equals將在下一篇文章中講解。