你不知道的東西! c# == 等於運算符 和 Object.Equals()


 最近在看 高級點的程序員必看的     CLR via C#    書中說解釋了 Object.Equals()  方法的實現, 其中具體的實現用的是 == 運算符 !

 以前就對 == 運算符 的具體實現  產生過疑惑 . 它到底對比的什么?

 今天剛好手頭的東西弄完了,而且還得強制加班中 !  所以就那今天的加班時間 來認真 來看一下 == 運算符 !

  •  最早對於 == 和 Object.Equals()  的了解是來源於 很早以前的一次面試 上面的面試題就有這個問題, 非常遺憾是 當時我水平有限 恰好不知道 怎么回答.  沒有回答上來 回家后開始 百度一下答案 看的是迷迷糊  糊的. 有好幾種說法. 當時也沒太在意  找個了最主流的 就認為是 "真理" 了!
  • 閱讀  CLR via C# 了解底層實現時 有看到了 Object.Equals()  看到了  內部使用的是 == 運算符 進行的比較   我對以前的標准答案 和 CLR via C# 書中所說的答案 都持有懷疑態度 然后找證據進行研究.我只相信我看到的. 我也慶幸自己 真的看到了.

因為是 Object.Equals() 這方法產生的疑問 所以吧這個內部實行帖出來

   

        public static bool Equals(object objA, object objB)
        {
            return ((objA == objB) || (((objA != null) && (objB != null)) && objA.Equals(objB)));
        }
// 他首先使用了 == 符號進行對比 然后又 使用對象自己 的
Equals 進行對比
 
        

 Ps   objA.Equals方法得解釋一下 .   objA使用的Equals方法是 類型自己可能會重寫 父類的 Equals 的這個方法.

 

等於到底做了什么? 

關於怎么實現的   非常抱歉的說  我並沒有找到 == 運算符的具體實現代碼. 

但是 可以再MSDN 上看到 它到底干了什么.

http://msdn.microsoft.com/zh-cn/library/53k8ybth.aspx

對於預定義的值類型,如果操作數的值相等,則相等運算符 (==) 返回 true,否則返回 false。 對於 string 以外的引用類型,如果兩個操作數引用同一個對象,則 == 返回 true 對於 string 類型,== 比較字符串的值。

通過MSDN 上的解釋 我們可以看到  好像是 它很厲害  能過自動通過類型 進行不同的對比.

其實並未如此     

    回到我們的更本問題  :  == 運算符 對比的到底什么?

    如果你了解    和 托管堆   那么你一定知道  數據 字節 一定不是放在 棧 中 就是放在 托管堆 中 所以 == 運算符 對比 不是對比的 棧 中的數據 就是對比的 托管堆中是數據 可以你覺得我說廢話了,但是還是要交代清楚:

我們來看一段簡單的代碼

 

   class Program
    {
        static void Main(string[] args)
        {
            Class2 s1 = new Class2();
            Class2 s2 = new Class2();
            Console.WriteLine(s1 == s2 ? "True" : "false");
        }
    }
    public class Class2
    {
    }

 大家猜一下  是  Ture 還是 False 

答案是 False ;    

想一下  如果是對比的是 托管堆中的話  那么他們都是創建了 該  對象的  "逐層父類"   "類型對象指針"  "同步快索引"  等 ; 

代碼中創建的是一樣的 那么 托管堆中的數據就是一樣的  如果是對比的 托管堆  那么返回就是 true  顯然 程序返回的是 False 

而且! 

值類型 是存放在 棧 中的 托管堆中並沒有東西. 所以 對比 只能是對比 棧中的數據.

 

說了一堆 就是想先解釋清楚 == 運算符對比的是 棧 為什么對比是 棧 而不是托管堆! 我喜歡說的詳細點 雖然你可以能覺得啰嗦,我也得巧更多的鍵盤.但是我認為只有寫的很詳細了 觀看的人才會容易理解.

 

下面做一些 MSDN 上 == 運算符 對比類型的解釋

  • 值類型,MSDN上是 預定義的值類型  其實就是他本身只帶的值類型 上面的結論是 == 對比是 棧 值類型的對比就沒什么說的了  
  • string 以外的引用類型  他們都有一個特定 那就是他們都是Object 的派生類.   棧中 放入的是 托管堆中的是 地址指向.   那么棧中是 地址指向  由於== 是對比的棧  后面的就不用說多了吧
  • string 類型   都說它是個特殊類型    我覺得是以為 微軟對其做的  "stirng池"   定義一個 string  變量 並且給 它賦值的時候 會先檢查  "stirng池" 中是是否已經有了 同樣的的內存數據. 如果True 則 將這個內存地址 的指針 賦值給 棧中的變量名稱. 

          string 類型 重寫了 == 運算符 將其使用了 string 類重寫的 Equals 方法進行對比;

          具體代碼段: 

        public static bool Equals(string a, string b)
        {
            return ((a == b) || (((a != null) && (b != null)) && EqualsHelper(a, b)));
        }  
        public static bool operator ==(string a, string b)
        {
            return Equals(a, b);
        }
        
        public static bool operator !=(string a, string b)
        {
            return !Equals(a, b);
        }

        關鍵是 EqualsHelper  這個方法

          

 private static unsafe bool EqualsHelper(string strA, string strB)
        {
            int length = strA.Length;
            if (length != strB.Length)
            {
                return false;
            }
            fixed (char* str = ((char*) strA))
            {
                char* chPtr = str;
                fixed (char* str2 = ((char*) strB))
                {
                    char* chPtr2 = str2;
                    char* chPtr3 = chPtr;
                    char* chPtr4 = chPtr2;
                    while (length >= 12)
                    {
                        if (((*(((long*) chPtr3)) != *(((long*) chPtr4))) || (*(((long*) (chPtr3 + 4))) != *(((long*) (chPtr4 + 4))))) || (*(((long*) (chPtr3 + 8))) != *(((long*) (chPtr4 + 8)))))
                        {
                            break;
                        }
                        chPtr3 += 12;
                        chPtr4 += 12;
                        length -= 12;
                    }
                    while (length > 0)
                    {
                        if (*(((int*) chPtr3)) != *(((int*) chPtr4)))
                        {
                            break;
                        }
                        chPtr3 += 2;
                        chPtr4 += 2;
                        length -= 2;
                    }
                    return (length <= 0);
                }
            }
        }

它將其每個字符串全部取出單個進行對比.     所有 CLR via C# 中 說是對比的 同等性  

 

現在 == 運算符的 問題都解開了疑惑了

那么讓我們重新看一下 Object.Equals 這個方法

         public static bool Equals(object objA, object objB)
        {
            return ((objA == objB) || (((objA != null) && (objB != null)) && objA.Equals(objB)));
        }

這段代碼我們前面就看過 是 Object.Equals 是代碼,  讓我們再次解釋一下  

  •  1 現將比對的對象封裝成 跟類型 也就是Object  類  在說一遍封裝 又得講很多 簡略一下   就是兩個對象   如果是值類型 就將其通過 分裝成Object 轉換為引用類型 將其通過==運算符 對比棧中的 指向內存的指針.    
                int s1 = 1;
                int s2 = 1;
                object o1 = (object) s1;
                object o2 = (object) s2;
                Console.WriteLine(o1==o2? "true":"fales");

    這段代碼返回的永遠都是 Fales 

  • 2  然后進行 是否非空的驗證  
  • 3 調用 objA 對象的Equals 方法 . 這里是關鍵 !  因為這里調用的並不是 Object類的 Equals 方法 而是當前傳遞過來的對象類  類型的Equals 方法!
    幾乎所有 類型都會重寫 Object 根類的 Equals 即便是當前對象沒有重寫 那么父類也會重寫Object 的 Equals 方法 如 所有值類型對象的根類  System.ValueType   重寫了 Equals 方法
        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }
            RuntimeType type = (RuntimeType) base.GetType();
            RuntimeType type2 = (RuntimeType) obj.GetType();
            if (type2 != type)
            {
                return false;
            }
            object a = this;
            if (CanCompareBits(this))
            {
                return FastEqualsCheck(a, obj);
            }
            FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            for (int i = 0; i < fields.Length; i++)
            {
                object obj3 = ((RtFieldInfo) fields[i]).InternalGetValue(a, false);
                object obj4 = ((RtFieldInfo) fields[i]).InternalGetValue(obj, false);
                if (obj3 == null)
                {
                    if (obj4 != null)
                    {
                        return false;
                    }
                }
                else if (!obj3.Equals(obj4))
                {
                    return false;
                }
            }
            return true;
        }

   這是具體實現代碼  看的很明顯 是通過反射獲取對象的值 進行對比 也就的對比的相等性

但是 ! 我們經常使用的 一些 .net預定義的類 幾乎還會接着重寫這個方法 

如 int32

        [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        public override bool Equals(object obj)
        {
            return ((obj is int) && (this == ((int) obj)));
        }
        
        [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        public bool Equals(int obj)
        {
            return (this == obj);
        }

代碼如上  可以清楚的看到 它並未  獲取對比類型Type 搜索所以字段 屬性 獲取值對比等一系列復雜的 邏輯  就用了 ==  運算符進行的對比 為什么 你們自己想去 懶得說了. 系能是一方面 更本原因還是因為預定義的值類型 是棧中存放.

 

 

Ps:  寫了這么多 觀看的你 可以覺得我寫的啰啰嗦嗦的, 但我還是認為寫的詳細點 把 真實的源碼貼出來 才能徹底解決所產生的疑惑.  百度上看一眼 人家說的幾句話你就能真的明白了嘛?

如有哪里不對 還希望指正!  

本人渴望交流 和指點!

 

   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM