首先,假設大家已經知道了什麼是實值型別,什麼是參考型別,也明白實值型別的變數就是直接儲存在堆棧上,而參考型別是在堆棧上存放一個地址,這個地址就是參考型別變數的執行個體對象在託管堆上真正的位置。下面說幾點總結出來的小結論,有些不一定會有很詳細很深入的邏輯推理過程。
其次,推薦先看一下:《Effective C#》Item 9:區別和認識四個判等函數,http://www.cnblogs.com/tonytonglx/articles/2080726.html。裡面說到ReferenceEquals、靜態Equals,具體類型的Equals以及==操作符這四個判等函數,我下面說到是後兩個比較函數,即是具體類型的equals方法和==操作符。
1、ValueType是所有實值型別的基類,但ValueType本身也是整合於object;
2、object類型是所有類型的父類:包括所有實值型別和參考型別。但第一點說了,一般強調的是實值型別是繼承於ValueType,而參考型別是強調繼承於object。為什麼呢,因為ValueType在繼承object時改了很多東西,包括重寫了Equals方法、重載了==操作符,實值型別和參考型別的差異就是根源於此;
3、先說 == 操作符。對於實值型別,== 比較的就是兩個類型在數值上是否相等,簡單的數值上的比較,如 1==1.0 為真,這個沒有疑問。對於參考型別,== 比較的是兩個參考型別的變數是否指向同一個執行個體對象,效果等於與 referenceEquals();
4、再說equals。在object裡,public override bool Equals( Object obj ) 方法預設實現支援引用相等性(對於參考型別)和按位相等性(對於實值型別)。
引用相等性是指進行比較的多個對象引用所引用的是同一個對象。按位相等性是指進行比較的多個對象具有相同的二進位表示形式。請注意,按位相等和值相等是不同的,值相等性是指所比較的對象具有相同的值,即使它們具有不同的二進位表示形式也是如此。可簡單總結為:值相等只要兩個對象從數值角度上比較相等即可,而按位相等,兩個對象要是同一種類型,然後再從數值角度上比較是否相等。
所以,對於實值型別,調用其類型本身的equals方法時,會先判斷是不是同一種類型,是的話在判斷兩個值在數學上是否相等,是的話才返回true。如 int a = 1; a.Equals(1.0) 返回的將是false,因為1.0在C#裡預設是double類型的,和int類型的a不是同一類型,所以返回false,儘管它們在數值上是相等的。
而對於參考型別,equals預設比較的是引用相等性,和referenceEquals方法、==操作符的效果是一樣的;當然,你可以重寫equals方法。
總結:對於實值型別,==比較的是數值,equals先比較是不是同一類型,是的話再比較數值。對於參考型別,==和equals預設比較的都是 是否引用了同一個執行個體對象。
至於重寫equals方法或重載==操作符的原則,那就是另外的問題了,這裡不涉及。
下面說說string類型和object類型的特殊性
string類型的特殊性表現在 字串具有“不變性”,當CLR初始化時,它會建立一個內部的散列表,Key為字串,Value為指向託管堆中字串對象的引用。當構造一個新的string對象時,先會去散列表中查詢是否存在一個值和它相等的字串,如果存在,就直接將新的string對象指向那個字串,如果不存在那麼會在託管堆(heap)中構造一個新的String對象,然後將字串的值和指向該對象的引用添加到散列表中。
但是,動態建立的字串不會去查詢散列表,而是直接在託管堆中建立新的String對象。詳見http://www.cnblogs.com/qingxia/archive/2011/01/06/1928896.html
要想獲得可變字串的類型要用stringbuilder。
object類型也具有不變性。如下:
int a = 2; object obj1 = a; object obj2 = obj1; obj2 = 10; Console.WriteLine("{0}、{1}、{2}",a,obj1,obj2);
輸出:2、2、10.就是說obj2的改變並沒有影響到obj1。
Console.WriteLine(object.ReferenceEquals(obj1, obj2)); 會輸出false,更說明了obj1和obj2已經指向不同的執行個體對象了。
新手,歡迎各位討論,提出自己的看法。