1. <wbr id="m8vu6"></wbr>

      <del id="m8vu6"><center id="m8vu6"><source id="m8vu6"></source></center></del>
        <p id="m8vu6"><sub id="m8vu6"></sub></p>

        VB.net 2010 視頻教程 VB.net 2010 視頻教程 VB.net 2010 視頻教程
        SQL Server 2008 視頻教程 c#入門經典教程 Visual Basic從門到精通視頻教程
        當前位置:
        首頁 > 編程開發 > c#教程 >
        • C#教程之C#相等性 - “==”

        • 2019-04-13 19:06 來源:未知

        今天寫一下C#里的“==”這個操作符。

        原始類型

        假象

        在剛學C#的時候,我以為C#里的==和.NET里的object.Equals()方法是一樣的,就是一個語法糖而已。其實它們的底層機制是不一樣的,只不過它們給出的結果在大多數情況下恰好相同。

        看個例子:

        這倆方法給出的結果都是True。

        看起來這兩種方式做了同樣的動作,就是比較兩個值。

         

        底層原理

        Build項目,然后使用ildasm看一下生成的il語言(ildasm位置大致在:C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7.2 Tools)。

        使用ildasm打開生成的dll,首先查看Program類里面的ByEqualMethod方法:

        可以看到C#源碼里調用Equals()的地方直接被翻譯成il語言里相應的Equals()方法了。。。。

         

        然后看一下ByEqualOperator這個方法:

        在C#里該方法使用了==操作符,而在il語言里,我們只看到了一個叫做ceq的指令。ceq的意思是compare for equality,就是比較兩個值是否相等,在運行時,它將會被轉換為硬件上的比較,也許用的是CPU的寄存器。

        針對原始類型,C#的==操作符并沒有使用.NET里提供的那些Equals方法,這時==操作符使用專用的匯編語言指令來進行判斷相等性的

         

        使用 == 判斷引用類型的相等性

        這里的引用類型不包含string。

        看例子,這里我使用==來比較自定義類MyClass的兩個實例是否相等:

        而結果是兩個False:

         

        使用ildasm看一下ByEqualMethod()這個方法:

        可以看到,a.Equals(b)調用的是virtual的object.Equals()方法,參數類型是object,這個應該都能理解。

         

        再看一下ByEqualOperator()方法:

        == 操作符翻譯過來還是使用ceq對兩個參數進行的比較,和之前int類型的例子一樣,除了參數類型不同。

        所以這應該也是使用CPU的硬件來進行判斷相等性的,那么像這種引用類型是怎么通過CPU硬件來比較的呢?因為這兩個類型是引用類型,所以c1,c2兩個變量里面保存的是它們對應的實例在托管堆中的內存地址,也就是兩個數字而已,所以當然可以進行比較了。

         

        string

        我們都知道,==用來判斷string相等性的時候,比較的是string值,而不是引用地址。

        看例子:

        結果是兩個True:

         

        首先,使用string.Copy()方法可以保證str1和str2是兩個不同的引用。

         

        使用ildasm,先看ByEqualMethod():

        可以看到,這里a.Equals(b)實際調用的是string實現的IEquatable<T>接口的Equals方法,它的參數是string。

         

        再看一下ByEqualOperator():

        這次沒有使用ceq指令,而是調用了一個叫做op_Equality()的方法,這是個什么方法?

        其實它是C#里 == 操作符的一個重載:static bool op_Equality(string, string)。

         

        在C#里,當你定義一個類型的時候,你可以對==操作符進行重載,格式大概如下:

        因為il語言里沒有操作符的概念,而只有方法才能作為操作符的重載而存在于il里,所以這里使用的是靜態方法,它會被翻譯為一個特殊的靜態方法叫做op_Equality()。

         

        我們也可以直接看一下string類的源碼,里面也是這樣對==進行重載的:

        當然,重載了==,也需要重載 !=。

         

        小結

        總結一下,使用==來判斷引用類型的相等性,需要按下面的思路順序進行考慮:

        1. 該類型是否對 == 進行了重載?如果是,那就是用該重載方法;否則看2

        2. 使用ceq指令來比較引用指向的內存地址

         

        另外還需要再提醒一下的是,string類的==和Equals()方法永遠都會給出一樣的結果。

        還有一個原則就是,當你改變某個類型的相等性判斷方法是,要確保==和Equals()方法做的是同樣的事情。

         

        值類型

        非原始類型

        看例子,這里有兩個值類型:

        當我使用==對它們進行比較的時候,直接報錯了。

        因為默認情況下,不可以使用==來對非原始類型的值類型進行相等性判斷。要想使用==,就必須提供重載方法。

         

        Tuple

        直接看例子:

        結果如下:
         

        針對這兩個tuple,我做了三個相等性判斷,通過第一個ReferenceEquals方法我們可以知道這兩個tuple變量指向不同的實例。

        而tp1.Equals(tp2)返回的是True,這是因為Tuple類(引用類型)重寫了object.Equals()方法,從而比較的是Tuple里面的值。

        盡管微軟為Tuple把object.Equals()方法重寫了,但是它并沒有處理==操作符,所以==還是在比較引用的相等性,所以會返回False。

        這樣做確實挺讓人迷惑的。。。

         

        比較==和object.Equals()方法

         

        通常情況下,盡量使用==操作符,但是有時候==不行,需要使用object.Equals()方法,例如涉及到繼承或者泛型的時候。

         

        繼承

        直接看例子:

         

        這兩個字符串我做了4個相等性判斷,其結果為:

         

        無論是object的virtual Equals()方法,還是==操作符,還是object的static Equals()方法,都會返回True。

        但是我做一下小小的改動:

         

        我們看看結果會不會變:

         

        結果發生了變化,str1==str2這次返回了False。

        這是因為==操作符不是virtual的,它相當于是static的,而static的是無法virtual的。

        現在 str1 == str2 這句話,我們比較的是兩個類型為object的變量,盡管我們知道它們都是string,但是編譯器并不知道。而針對于非virtual的方法或操作符,到底調用哪個方法是在編譯時決定的,因為這兩個變量的類型是object,所以編譯器會選擇用來比較object的代碼,而object又沒有==操作符的重載,所以==做的就是比較引用的相等性,而這兩個string是不同的實例,所以結果會返回False。

        所以(object)x == (object)y和ReferenceEquals(x, y)的結果總是一樣的。

        針對涉及繼承的相等性判斷,最好還是使用object.Equals()方法,而不是==操作符。

         

        泛型

        另一種不適合使用==操作符的情景是涉及泛型的時候,直接看例子:

        這個泛型方法直接報錯了,因為==操作符無法應用于這兩個操作數T,T可以是任何類型,例如T是非原始類型的struct,那么==就不可用。我們無法為泛型指定約束讓其實現某個操作符。針對這個例子,我可以這樣做,來保證可以編譯:

        現在T是引用類型了,代碼可以編譯了。我們使用以下該方法:

        按理說這就相當于調用了Equals()方法,結果應該返回True。而實際結果是:

        之所以返回了False,是因為泛型方法里的==操作符比較的是引用,而這又是因為盡管編譯器知道可以把==操作符應用于類型T,但是它仍然不知道具體是哪個類型T會重載該操作符,所以它會假設T不會重載==操作符,從而對待這兩個操作數如同object類型一樣并編譯,所以判斷的是引用相等性。

        所以泛型方法不會選擇任何的操作符重載,它對待泛型類就像對待object類型一樣。

        綜上,針對泛型方法,應該使用Equals()方法,而不是==操作符。

         

        博客文章可以隨便轉載和抓取. 
        相關教程
        免费看成年人视频大全_免费看成年人视频在线观看