了解Equals所發生的事情

2021-09-01 15:59:23 字數 3197 閱讀 9564

首先,我推薦大家先看下equals 和 == 的區別一文再繼續往下看本文(雖然文中有些解釋還是讓人有點感覺不對),因為我就是從園友@一沐陽光的這篇文章中滋生出的問題,才寫的本文。當然你也可以直接看本文,因為我並沒有把本文與它強行聯絡在一起。

先來看下面的**:

1

int i1 = 8;2

int i2 = 8;3

bool bo1 = i1 == i2; //

true

4bool bo2 = (object)i1 == (object)i2; //

false

5bool bo3 = i1.equals(i2); //

true

6bool bo4 = i1.equals((object)i2); //

true

7bool bo5 = ((object)i1).equals(i2); //

true

1、bo1=true這點是沒有疑問的,值型別的比較,就是比較它的值。

2、bo2=false。這是把兩個int型的值型別裝箱了,然後「==」比較時,是比較其引用的位址,所以為false。

3、bo3=true。這是呼叫了int對應的int32的equals(int)方法:

1

public

bool equals(int

obj)

2

很顯然,this就是i1,obj就是i2,最終返回的還是i1==i2,為true。

4、bo4=true。可能我們經常都認為equals(object)是繼承自object類:

1

public

virtual

bool equals(object

obj)

2

但經常忽略了一點,它是個virtual方法,像int32、uint32、double、string等等常用型別,都是重寫了這個方法的,而int對應的int32型別重寫為:

1

public

override

bool equals(object

obj)

2

所以遇到i1.equals(i2),首先呼叫了(obj is int)來判斷i2是不是int型的,如果是就會有強制轉化,實際上執行返回的是 i1==(int)((object)i2) ,可見,這必然也是true。

其它型別的重寫也與這類似,都會首先判斷引數obj是不是當前型別,是則強制轉化,再進行比較,如果不是,則看情況進行處理。這裡說的看情況,主要包括了兩種情況:第一種,本身是值型別。嘗試轉化引數的型別,如果引數與本身型別相同,這時直接呼叫==號進行比較(也有特殊情況),如果引數與本身型別不同,則返回false;第二種,本身是引用型別(注意string是引用型別),會嘗試轉化引數為本身的型別,失敗則直接返回false,否則再比較引用的位址是否相同,進行返回。string是引用型別中的特例,不僅引用位址相同的string可以為真,內容相同的string比較也為真,可以看下面關於string型別的equals(object)重寫方法:

1

public

override

bool equals(object

obj)27

string strb = obj as

string;8

if (strb == null)9

12if (object.referenceequals(this

, obj))

1316

if (this.length !=strb.length)

1720

return equalshelper(this

, strb);

21 }

說明一下,最後的equalshelper(this,strb)函式內部就是對這兩個字串的內容進行逐字元地比較了。

5、bo5=ture。這與前面的bo3、bo4並不一樣了,這時編譯器呼叫的不是int32的equals(int)或equals(object)方法,而確實就是object型別的equals(object)方法,通過il**得知的,i1是通過(object)i1手動裝箱的,而i2則是協變,自動完成裝箱:

1   il_0032:  ldloc.0

// 讀取i0

2il_0033: box [mscorlib]system.int32     // 裝箱

3 il_0038: ldloc.1                     // 讀取i1

4il_0039: box [mscorlib]system.int32 // 裝箱

5 il_003e: callvirt instance bool [mscorlib]system.object::equals(object

) // 呼叫object.equals(object)方法

6 il_0043: stloc.s bo5                // 儲存結果到bo5

那麼此時為什麼又相等了,注意前面貼的關於object的**中,只有一行:runtimehelpers.equals(this, obj);這句話我使用reflector也沒有找到具體實現,目測應該是用於幫助編譯器實現執行時**生成工作的,在其內部可能實現了轉化,會在執行期根據i1的型別,再呼叫int32的equals方法來完成最終比較。為了驗證我的想法,我在msdn上找到了下面關於runtimehelpers.equals(object, object)的話:

提供一組為編譯器提供支援的靜態方法和屬性。無法繼承此類。

返回值如果 o1 引數與 o2 引數是同乙個例項,或者二者均為 null,或者 o1.equals(o2) 返回 true,則為 true;否則為 false。

由於(object)i1與(object)i2並不是同乙個例項,二者又均不為null,所以進入i1.equals(i2)的邏輯運算階段,最終返回true。本文就此完結,這樣一來,結合最開始貼出的文章,最少我自己已經能大概搞清楚實際發生了什麼,而不需要去死記了,也不會在遇到這種情況時不知所措了。

ArrayList需要了解的事情

arraylist 是日常開發經常使用到的容器類。它能夠方便的進行資料的查詢 替換。但是因為其低層實現的原因在資料容量 效能 執行緒安全上都存在問題,主要涉及到下面的內容 1 預設初始容量為 0,如果未指定容量則首次初始的容量為 10 同時其也是有容量限制的 2 新增元素會涉及到陣列擴容和陣列元素拷...

2014暑假總結 美好的事情即將發生

暑假快結束了,總結這一年的收穫吧,這一年過的特別快,沒有就一年過去了,可是收穫卻不少。總結分享一下。潤物細無聲 公尺老師常常說他的學習方法是潤物細無聲的,這一點自己的感受特別多。今年一開始做的人事項目的時候,剛開始時不自信還是存在的,可是慢慢的發現自己的對於整個系統的認識和 編寫上手還是非常快的,自...

STL 迭代器刪除,插入元素發生的事情

一,序列式容器 1,刪除迭代器指向的元素 對於序列式容器 vector deque list 刪除當前的iterator會使後面所有元素的iterator都失效。這是因為vector,deque使用了連續分配的記憶體,刪除乙個元素導致後面所有的元素會向前移動乙個位置。使用erase方法後,返回的是下...