1. 程式人生 > 其它 >c#中==與Equals區別

c#中==與Equals區別

一、過載和重寫的呼叫區別

==是方法過載(引數個數或引數型別不同),在實際呼叫中會根據引用型別(不是實際物件)來呼叫,如Father f = new Son() 通過f會呼叫到 ==(Father, Father)過載,而不是==(Son, Son)過載

Equals是方法重寫,在實際呼叫中會根據實際物件(不是引用型別)來呼叫,如Father f = new Son() 通過f會呼叫到 Son.Equals(object)方法,而不是Father.Equals(object)方法

二、所有型別基類object和值型別基類ValueType

首先對於基類object,它的==是進行同一性的比較,即比較是否是同一個物件,而在其Equals的實現中,也是直接呼叫的==,所以對於object,它的==和equals都是進行的同一性的比較

在值型別基類ValueType,雖然是繼承自object,但是它有自己的==過載以及重寫了Equals方法,使其進行相等性比較,即比較內容是否一致,但它的Equals重寫是通過反射實現的,速度較慢,所以當我們自定義其他值型別(如struct)時,強烈建議我們重寫自己的Equals方法,而不是用基類ValueType的

PS:為什麼要用反射?因為ValueType不知道我們的值型別到底有哪些欄位,所以需要通過反射將所有欄位取出來然後逐一進行相等性比較

三、值型別與引用型別

對於值型別,我們需要自己手動過載自己的==,如果我們不過載而直接使用==,會編譯不通過,例如float、double實現中都有過載自己的==的,過載後的==是進行相等性比較,而Equals方法由於繼承了ValueType的Equals方法,也是進行的相等性比較,所以我們可以不重寫(但一般都會重寫,原因前面已經提到了=。=),例如float、double實現中也是有重寫自己的Equals方法的,同時還會定義一個引數為當前型別的Equals方法,如Double類中public bool Equals(Double obj) 和public override bool Equals(object obj)

對於引用型別string,雖然它是引用型別,但是因為它過載了自己的==,和重寫了自己的Equals,所以它的Equals和==都是相等性比較

對於其他引用型別,比如我們自定義的類,因為都是隱式繼承自object的,在沒有重寫Equals和過載==的情況下都是和object一樣進行的同一性比較,但一般涉及比較介面時,我們都會在自定義的引用型別中重寫我們自己的Equals和過載==

特殊的,比如一個Int或者string型別賦值給object,這時去比較==和Equals是什麼情況呢?

String s1 = new String("aa");
String s2 = new String("
aa"); string s3 = "bb"; string s4 = "bb"; int i1 = 1; int i2 = 1; object o1 = s1; object o2 = s2; object o3 = s3; object o4 = s4; object o5 = i1; object o6 = i2; Console.WriteLine(o1==o2) //False Console.WriteLine(o1.Equals(o2)) //True Console.WriteLine(o3==o4) //True Console.WriteLine(o3.Equals(o4)) //True Console.WriteLine(o5==o6) //False Console.WriteLine(o5.Equals(o6)) //True

如果不明白,建議詳讀【一、過載和重寫的呼叫區別】

當然這裡還涉及到另一個知識點:字串常量與字串變數的區別

字串常量:形如我們通過string s1 = "bb" string s2 = "bb" 這時我們的s1和s2是指向同一個地址,這是因為它的初始值是一個常量,所以其地址分配在託管堆上的靜態儲存區,即所謂的常量池

字串變數:形如我們通過String s1 = new String("aa") String s2 = new String("aa") ,這時我們的s1和s2指向的地址不一樣,這是因為這是在託管堆上動態分配的地址

以上便是本人最近研究的關於c#的==和Equals的相關總結,如有不對之處,還望多多指正!

參考博文:C#中 == 與 Equals區別 C# 中 Equals 和 == 的奇怪問題