淺析C#中的等號“==”和Equals
1.“==”和Equals兩個真的有關聯嗎?
對於“==”和Equals大多數網友都是這樣總結的:
- “==” 是比較兩個變數的值相等。
- Equals是比較兩個變數是否指向同一個物件。
public class Person { public Person(string name) { this.Name = name; } public string Name { get; set; } } static void Main(string[] args) { string a = new string(new char[] { 'h', 'e', 'l', 'l', 'o' }); string b = new string(new char[] { 'h', 'e', 'l', 'l', 'o' }); Console.WriteLine(a == b); //true Console.WriteLine(a.Equals(b)); //true object g = a; object h = b; Console.WriteLine(g == h); //false Console.WriteLine(g.Equals(h)); //true Person p1 = new Person("jia"); Person p2 = new Person("jia"); Console.WriteLine(p1 == p2); //false Console.WriteLine(p1.Equals(p2)); //false Person p3 = new Person("jia"); Person p4 = p3; Console.WriteLine(p3 == p4); //true Console.WriteLine(p3.Equals(p4)); //true Console.ReadKey(); }
假如上述結論正確,“==” 是比較兩個變數值相等,那麼下面這句程式碼就不應該為True.
Console.WriteLine(a == b); //true
很明顯,上面的兩個字串變數:a,b 是指向兩個不同的物件,即它們在棧空間儲存的記憶體地址也是不同的。但為毛它們就相等了呢?
2.什麼是運算子過載?
運算子過載,就是對已有的運算子重新進行定義,賦予其另一種功能,以適應不同的資料型別。打個簡單的比方:“+” 運算子,在“+” 兩邊全為數值型別的變數時,“+” 運算子表示數學上的“+” 意義。若“+” 運算子兩邊只要有一個為字串型別,那麼“+” 運算子就表示連線字串的意義。這樣的運算子過載例項有很多,那麼這跟本文主題有毛關係?我想說的是,上面字串變數:a , b 就是因為String類過載了運算子 “==”,看下列原始碼:
public static bool operator == (String a, String b) { return String.Equals(a, b); } public static bool operator != (String a, String b) { return !String.Equals(a, b); }
很明顯String類中真的過載了“==”運算子,並且不止 “==” 還有 “!=” 哦。並且在過載運算子方法內部直接呼叫String類中的Equals方法,原始碼如下:
public static bool Equals(String a, String b) { if ((Object)a==(Object)b) { return true; } if ((Object)a==null || (Object)b==null) { return false; } if (a.Length != b.Length) return false; return EqualsHelper(a, b); }
由上可得:“==” 運算子並不一定是比較兩個變數中儲存的值是否相等,這要看當前運算子在當前這個型別中是否寫有過載。
3.Equals的重寫
還是上面例子:
string a = new string(new char[] { 'h', 'e', 'l', 'l', 'o' }); string b = new string(new char[] { 'h', 'e', 'l', 'l', 'o' }); Console.WriteLine(a == b); //true Console.WriteLine(a.Equals(b)); //true
由上可知:a ,b 為兩個不同的物件。但Equals為True,則上述:“Equals是比較兩個變數是否指向同一個物件“這一結論不成立。原因看String類中的Equals方法:
public override bool Equals(Object obj) <br> { if (this == null) //this is necessary to guard against reverse-pinvokes and throw new NullReferenceException(); //other callers who do not use the callvirt instruction String str = obj as String; if (str == null) return false; if (Object.ReferenceEquals(this, obj)) return true; if (this.Length != str.Length) return false; return EqualsHelper(this, str); } public bool Equals(String value) <br> { if (this == null) //this is necessary to guard against reverse-pinvokes and throw new NullReferenceException(); //other callers who do not use the callvirt instruction if (value == null) return false; if (Object.ReferenceEquals(this, value)) return true; if (this.Length != value.Length) return false; return EqualsHelper(this, value); }
由上面可知String類中不僅重寫了Object中的Equals還有自己的Equals方法,但是實現程式碼幾乎是一樣的。比較型別,記憶體地址,實際值,從而獲得最終的結果。所以Equals不一定就是單一的比較引用地址是否相同,更何況我們還可以重寫和自定義。但是重寫Equals也有需要注意的地方,就是如果你需要用到HashMap,HashSet,Hashtable那麼你也需要重寫GetHashCode()。
4.為什麼有了“==”還要有Equals?
中國有一句話:“任何事物的存在必然有他存在的道理和價值”,同理“==”和Equals也是一樣。“==” 在引用型別中最基本的實現就是去比較兩物件的記憶體地址是否一致,一致則相等反之則不等。這樣的實現很明顯是從硬體角度去思考的,如果兩個物件相等即為同一個物件,那麼它們在記憶體中的地址必然相等。但很多時候 “行為(方法)“ 是取決於我們去觀察世界的角度。 如:String型別我們申明一個字串更在意的是字串所具有的實際值,而不是在意兩個物件在記憶體中是建立了一次還是兩次(即記憶體地址是否相等),只要它們所具有的實際值是相等的那麼我們就認為它們是相等,這是從生活業務邏輯中去理解的而不是從機器角度上去理解的。當然上面宣告相同的字串變數是建立一次還是兩次我想:” 常量池(或字串拘留池)“ 已經給了我們最好的解決方案。
5.“==”和Equals到底什麼關係?
”==“ 運算子和Equals它們其實是互補關係。因為:”==“ 運算子主要實現形式是站在 ”計算機角度(或者說硬體角度)” 上去實現的,而Equals是站在常用的業務場景或者是特定的業務場景下去實現的,二者沒有什麼必然的聯絡,是根據自己的業務需要選擇不同方法而已。所以Object裡面的Equals是Visual,很多類中都重寫了它,並真正達了在當前型別中所需的特定行為,即:多型。所以就不難解釋上面:
object g = a; object h = b; Console.WriteLine(g == h); //false Console.WriteLine(g.Equals(h)); //true
因為Object中沒有實現過載運算子:“==”,所以當前“==”的比較方式是比較兩變數在棧空間儲存的記憶體地址是否相同。而Equals則是呼叫String類中的Equals,原因g變數在執行中它實際上指向一個字串物件的,而當前的Object型別只是Visual studio和編譯器的行為,即:還是多型。
最後任何東西都有它的規則:”==“和Equals也不例外,詳細資料請點選:跳轉至MSDN。
相關文章
- C#基礎之==(雙等於號)與equals()區別C#
- C# 中的 == 和 equals()有什麼區別?C#
- angular中的概念和原理淺析Angular
- 你不知道的東西! c# == 等於運算子 和 Object.Equals()C#Object
- 淺析C#程式設計中的記憶體管理C#程式設計記憶體
- 淺析mybatis中${}和#{}取值區別MyBatis
- 淺析c++11中的“=default“和“=delete“C++delete
- 淺析package.json中的devdependencies 和 dependenciesPackageJSONdev
- C# 列舉常用方法淺析C#
- C# Task若干問題淺析C#
- java 中equals和==的區別Java
- Java中equals和==的區別Java
- 淺析java中的IO流Java
- 淺析 JS 中的 Event LoopJSOOP
- LINUX 中的mmap淺析Linux
- react中 受控元件和 非受控元件 淺析React元件
- Java中 equals() 方法和 == 的區別Java
- 在java中“equals”和“==”的區別Java
- 淺析nodejs中的stream(流)NodeJS
- 淺析js中的Object.create()JSObject
- java中的JAR檔案淺析JavaJAR
- 淺析vue中的元件通訊Vue元件
- java中的反射機制淺析Java反射
- C#之Equals與==C#
- 淺析遊戲中的教程引導和章節連結遊戲
- 淺析TCP和nodejs中TCP的簡單應用TCPNodeJS
- redux中間鍵淺析Redux
- 淺析Spring的IoC和DISpring
- Qt自定義訊號槽的使用淺析+例項QT
- C#中的物件深複製和淺複製C#物件
- Java中equals和==比的是什麼Java
- 淺析Java中的執行緒池Java執行緒
- 淺析InnoDB引擎的索引和索引原理索引
- JavaScript兩個等號和三個等號區別JavaScript
- 淺談C#中重寫和隱藏的區別C#
- javaSE中的==和equals的聯絡與區別Java
- 淺析 C# 控制檯的 Ctrl+C 是怎麼玩的C#
- Netty 中的記憶體分配淺析Netty記憶體