顯然這是兩個用的對數據經行比較的方法。
但兩者是有區別的,熟悉C/C++的朋友們一定有對地址和值這兩個概念經行比較深入的研究。但是C#為了安全起見,把地址(也就是指針)這個東西給取消了,取而代之的是對象的引用(其實這個也是在棧上的和地址所處的地方是一樣的)。好了,現在我們來看==和equals的區別。
1.從最簡單的值類型入手
1 int a = 1; 2 int b = 1; 3 Console.WriteLine(a.Equals(b)); 4 Console.WriteLine((a == b).ToString());
結果是:True True
這是很顯然的,因為==在值類型中是有明確意義(關於這點是與引用對象有區別的)的,equals同樣也是比較兩者的值(equals的具體比較方法我將在后面說到)是否一樣,這里沒有涉及到地址的問題。
同理,其他值類型都是一樣的(string 這個類有點例外,待會再最后我將詳細說的)。
2.對於引用類型
1 class Person 2 { 3 public string Name 4 { 5 get; 6 set; 7 } 8 } 9 static void Main(string[] args) 10 { 11 Person per1=new Person(){Name="zhangsan"}; 12 Person per2=new Person(){Name="zhangsan"}; 13 Console.WriteLine((per1 == per2).ToString()); 14 Console.WriteLine(per1.Equals(per2)); 15 }
結果是:False False
這里結果都是False,在這它們的比較的機制也差不多的 == 是比較的引用的地址是否相等,equals是看兩個引用變量是否引用同一個對象。說白了,就是看他們是不是一個“人”;
3.對於“特殊”類型
1 static void Main(string[] args) 2 { 3 string str1 = "zhangsan"; 4 string str2 = "zhangsan"; 5 Console.WriteLine(str1 == str2); 6 Console.WriteLine(str1.Equals(str2)); 7 Console.WriteLine((object)str1==(object)str2); //這里是比較它們的地址值 8 }
結果是:True True True
到這里讀者可能就有疑問了,為什么str1和str2的地址也是一樣的呢。
好,下面我就幫大家系統的分析下上面所有代碼產生的結果的原因了。
1.對於equals
查看MSDN中equals方法中有這么一句話:
Equals 的默認實現支持引用相等性(對於引用類型)和按位相等性(對於值類型)。引用相等性是指進行比較的多個對象引用所引用的是同一個對象。按位相等性是指進行比較的多個對象具有相同的二進制表示形式。
這句話說明了什么?不就是說明了它的比較機制嗎。對於值類型的它是按位進行比較的,對於引用類型的它是看他們是否指向同一個對象。
關於equals的比較方法也就明了了。
2.對於==
在C#中,很多引用類型都是重載了==運算符的,對於引用對象,對象是在堆上建立的,而引用變量的值(也就是堆上對象的地址)是在棧上的。
而==就是比較棧上的內容是否相同的。
3.對於“特殊類型”
這里也就是比較特殊的地方 而且是單對string類來說的。
下面我加一段代碼:
1 string str1 = "zhangsan"; 2 string str2 = "zhangsan"; 3 string str3 = new string(new char[] { 'z', 'h', 'a', 'n', 'g' }); 4 string str4 = new string(new char[] { 'z', 'h', 'a', 'n', 'g' }); 5 Console.WriteLine("str1 == str2 " + (str1 == str2).ToString()); 6 Console.WriteLine("str1 Equals str2 " + str1.Equals(str2)); 7 Console.WriteLine("(object)str1 == (object)str2 " + ((object)str1 == (object)str2).ToString()); 8 Console.WriteLine("str3 == str4 " + (str3 == str4).ToString()); 9 Console.WriteLine("str3 Equals str4 " + str3.Equals(str4)); 10 Console.WriteLine("(object)str3 == (object)str4 " + ((object)str3 == (object)str4).ToString());
運行結果是這樣的:
前5個全是True 只有最后一個是False
原因是這樣的:
String類有很多特別的地方,其中之一就是它是“不會改變的”(immutable)。這說明在我們每次對一個String對象進行操作時(比如說使用Trim,Replace等方法),並不是真的對這個String對象的實例進行修改,而是返回一個新的String對象實例作為操作執行的結果。String對象的實例一經生成,到死都不會被改變了!
基於String類這樣的特性,CLR讓表示相同的字符串實際值的變量指向同一個String事例,就是完全合理的了。因為利用任何一個對String實例的引用所進行的修改操作都不會切實地影響到該實例的狀態,也就不會影響到其他所有指向該實例的引用所表示的字符串實際值。CLR如此管理String類的內存分配,可以優化內存的使用情況,避免內存中包含冗余的數據。
(這段話摘自http://www.cnblogs.com/instance/archive/2011/05/24/2056091.html)
簡單點說就是,既然string 着各類這個類不可變,把我們就讓同樣的字符串,都指向同一個堆上的資源,這樣不就節省的內存資源嗎?何樂而不為。
所以CLR就維護了一個暫存池(intern pool)在里面就有所有的已使用的字符串資源。
但是並不是所以相同的字符串都是指向同一資源的,只有那些顯式地使用字面量來賦值的時候它才會自動去檢查有沒有同樣字符串的資源,就像前面的“zhangsan"一樣。而使用
string str4 = new string(new char[] { 'z', 'h', 'a', 'n', 'g' }); 就不會檢查了。
如果你任然想讓它去檢查的話,可以使用Intern類的方法。
比如這里改成
string str4 = string.Intern(str3);
這樣他兩就指向相同的資源了。