【ASP.net】Equals 和 == 的區別

qq_23944441發表於2018-05-21

轉載:https://www.cnblogs.com/allenhua/p/5252473.html


在比較Equals 和 ==的區別前。我們先來了解下相關的知識

C#資料型別

1、值型別

值型別有:

  值型別包括:簡單型別、結構型別、列舉型別;引用型別包括:Object 型別、類型別、介面、代表元、字串型別、陣列。

  byte(1)、sbyte(1)、short(2)、ushort(2)、int(4)、uint(4)、long(8)、ulong(8)、float(4)、double(8)、decimal(8)、char、bool、列舉、結構。

  上述中括號中的數字表示位元組數,byte、ushort、uint、ulong 為無符號型別(沒有負數),順便說一下 sbyte 是有符號的位元組。

 

2、引用型別

  引用型別有:物件型別、類型別、介面、代表元、字串型別、陣列。

 

new 運算子

在 C# 中, new 關鍵字可用作運算子、修飾符或約束。

  1、 new 運算子

    用於建立物件和呼叫建構函式。

  2、new 修飾符

   用於向基類成員隱藏繼承成員。

  3、new 約束

   用於在泛型宣告中約束可能用作型別引數的引數的型別。

 

需注意一點:凡是用new運算子就會在堆記憶體中開闢空間存放引用(引用地址存放在棧上)物件的值

 

      ==  和 Equals 的區別

  1. == 是一個運算子。

  2.Equals則是string物件的方法,可以.(點)出來。 

 

我們比較無非就是這兩種 1、基本資料型別比較  2、引用物件比較 

1、基本資料型別比較

  ==和Equals都比較兩個值是否相等。相等為true 否則為false;

 

2、引用物件比較  

  ==和Equals都是比較棧記憶體中的地址是否相等 。相等為true 否則為false;

 

需注意幾點:

  1、string是一個特殊的引用型別。對於兩個字串的比較,不管是 == 和 Equals 這兩者比較的都是字串是否相同;

  2、當你建立兩個string物件時,記憶體中的地址是不相同的,你可以賦相同的值。所以字串的內容相同。引用地址不一定相同,(相同內容的物件地址不一定相同),但反過來卻是肯定的;

  3、基本資料型別比較(string 除外) == 和 Equals 兩者都是比較值;

 

 接下來我們來挨個挨個看例項:

  1、基本資料型別比較:==  和 Equals 都比較具體的值

int i1 = 8;  
int i2 = 8;  
bool bo1 = i1 == i2; //true  
bool bo2 = (object)i1 == (object)i2; // 裝箱後變成引用型別,引用不同 故為false 
bool bo3 = (i1).Equals(i2); //true 比較的是值 裝箱後 Equals比較的仍是值

2、來看一個地址不同,內容相同的比較。== 比較地址, Equals比較值

//使用了new運算子就會在記憶體中建立物件,地址是不相同的。字串內容相同            
string str3 = new string(new char[] { 'a', 'b', 'c' });   
string str4 = new string(new char[] { 'a', 'b', 'c' });  
bool b3 = (object)str3 == (object)str4; //false  裝箱後  是比較地址,這裡記憶體中的地址是不相同的 
bool b4 = ((object)str3).Equals((object)str4); //true 此時Equals依然是比較值

 

 

3、string字串比較:==  和 Equals 同樣也是比較具體的值

    //string是特殊的引用型別。只要是字串比較,不管是"=="還是"Equals"都是比較字串的內容

string str1 = "abc"; 
string str2 = "abc";
bool b1 = str1 == str2;  //true
bool b2 = str1.Equals(str2); //true
//使用了new運算子就會在記憶體中建立物件,地址是不相同的。字串內容相同
string str3 = new string(new char[] { 'a', 'b', 'c' }); 
string str4 = new string(new char[] { 'a', 'b', 'c' }); 
bool b3 = str3 == str4; //true  字串比較 是比較具體的值,這裡記憶體中的地址是不相同的 
bool b4 = str3.Equals(str4); //true 
bool b5 = str1 == str3; //true
bool b6 = (str1).Equals(str3); //true
object str5 = "abc";  //定義一個變數 str5並賦值為 abc
object str6 = "abc";  //定義一個變數 str6並指向字串abc
bool b7 = str5 == str6;  //true 
bool b8 = (object)str5 == (object)str6;  //但這樣就是比較引用是否相同,因為str5 和 str6 引用同一個例項 即為 true
bool b9 = str5.Equals(str6); //true
 

在上個列子中b3和b4都是true,我們來看看str3和str4在記憶體中的地址是否相等?其實是不相等的。用new操作符建立的物件都會在記憶體中分配一個新的記憶體地址,同時也可以證明這裡比較的是值,而不是地址;更進一步證明相同內容的物件地址不一定相同。

 

如何調出記憶體視窗?如圖:(啟動除錯後,這裡可以調出多個記憶體視窗)用來比較地址


4、引用型別比較: ==和Equals都是比較棧記憶體中的地址是否相等 

class MyClass
{
    public string name; 
    public MyClass(string name)
    {
        this.name = name; 
    }
}   
//------------------------測試-----------------------------
MyClass str7 = new MyClass("abc"); 
MyClass str8 = new MyClass("abc"); 
bool b10 = str7 == str8;  
//這裡是引用型別比較(不是string字串比較)所有比較是不是對同一個物件的引用,這裡顯然是記憶體中兩個不同的引用物件。故為false 
bool b11 = str7.Equals(str8); //同上
MyClass str9 = str7; //str7的地址賦值給str9。
bool bl2 = str7 == str9;  //true 引用同一個物件
bool bl3 = str7.Equals(str9);  //true
object str10 = new object();
object str11 = new object(); 
bool b12 = str10 == str11;  //false
bool b13=str10.Equals(str11);  //false

 同樣我們開看看str7 str8 str9 分別在記憶體中的地址,紅框標記(str7,str9)可以看出地址是相等的

 

 

看到這裡也許你疑惑了,既然引用型別比較 == 和Equals都是比較地址,那就沒區別嗎?

 Equals是string類下的一個方法,既然知道它的來路。我們就可以看看它內部原始碼的實現,開啟Reflector查詢string。找到Equals(Object obj) 和Equals(string value)

看到內部也是用==來實現的,並且看到Object的比較是轉換成string後比較。

 

好了,繼續來講,在引用型別比較中,== 和Equals都是比較地址外,Equals突出的地方。一個物件除了地址相同。還有其他獨有的特徵。比如我們要比較某人都來自中國(記憶體地址),並且都來自中國香港(記憶體地址獨有的特徵或性質),

當符合這兩個條件我們才說明這個物件是相等的。那麼此時 == 和Equals都無能為力。但Equals有它鶴立雞群的地方。

檢視Equals定義會發現,Equals是virtual方法:  public virtual bool Equals(object obj); 既然是virtual我們就可以重寫Equals方法,來編寫自己的業務邏輯

嗯!來編寫一個測試類,並且override Equals方法

class MyClass
{
    public string country; //國家
    public string city;  //城市
    public MyClass(string country, string city)     
    {
            this.country = country; 
        this.city = city;
    }
    public override bool Equals(object obj)
    {
        string ci = ((MyClass)obj).city;
        string co = ((MyClass)obj).country; 
        if (ci == "香港" && co == "cn") return true;
        return false; 
    } 
}

 

 測試:

MyClass str7 = new MyClass("cn","香港");
MyClass str8 = new MyClass("cn","香港");
MyClass str18 = new MyClass("cn", "臺灣");
bool na = str7.Equals(str8); //true
bool an = str7.Equals(str18); //false
 

最後談談這兩個有什麼區別:

string str1 = "abc";  
string str3 = new string(new char[] { 'a', 'b', 'c' });
 

以上這兩句話大家應該都見得多,比如str3這句筆試題居多,大體都是建立了幾個string物件。回答理所當然是兩個。一個是存放在堆記憶體上資料"abc",一個是存在棧記憶體上對"abc"的引用,通俗一點就是棧上的引用地址指向堆中的值或內容。

來看看str1這句,這並沒有建立物件。即沒有在記憶體中分配空間。因為沒有使用new運算子,這裡僅僅是宣告瞭一個指向物件的引用變數str1,暫時指向"abc",所有這裡只建立了一個string物件,所有要區分new的方式

看看下面這個列子是建立了3個物件。因為字串是不可變的

string x = "ab";
string y = "a"; 
string x = x + y;
 

不知道C#中有沒有常量池這一概念。還有看到網上很多這樣的 string str = new string("abc") 可我真的沒找到stirng的這個過載函式

 


相關文章