C#相等性 - “==”

来源:https://www.cnblogs.com/cgzl/archive/2019/04/13/10659745.html
-Advertisement-
Play Games

今天寫一下C#里的“==”這個操作符。 原始類型 假象 在剛學C#的時候,我以為C#里的==和.NET里的object.Equals()方法是一樣的,就是一個語法糖而已。其實它們的底層機制是不一樣的,只不過它們給出的結果在大多數情況下恰好相同。 看個例子: 這倆方法給出的結果都是True。 看起來這 ...


今天寫一下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()方法,而不是==操作符。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • PyQtdeploy 用戶指南 目錄 "介紹" "與V1.0+的差異" "作者" "證書" "安裝" 部署過程概覽 PyQt的演示 構建演示 Android IOS Linux MacOS Windos 構建系統根目錄 標準組建插件 創建Sysroot規範文件 PyQt演示的Sysroot 命令行 ...
  • 使用pip3 安裝cv2包的時候報錯 解決方案: pip3 install opencv-python ...
  • 本篇博文主要記錄網路編程的先修知識,重點講解了五層網路體繫結構和TCP/IP協議 ...
  • 上節課複習: 今日內容: 編程語言: python語言介紹: python解釋器安裝 環境變數: python代碼執行(兩種方式): pip : 應用程式使用文件的三步驟: 變數: 變數的三大組成: 比較: pycharm ...
  • 上一篇小樂帶大家瞭解了Java新特性之Stream,接下來將會繼續述說Java新特性之Optional Optional<T>類(java.util.Optional)是一個容器類,代表一個值存在或不存在,原來用null表示一個值不存在,現在Optional可以更好的表達這個概念。並且可以避免空指針 ...
  • selenium確認進入了預期頁面 在自動化操作中,瀏覽器每次進入一個新的需要,都需要確認該頁面是否打開或打開的頁面是否是預期的頁面 需要進行確認頁面後方可進行下一步操作 確認頁面有很多中方法,像每個頁面都有一個固定屬性(ng-page = ‘xxx’)來確認,所以確認頁面的時候就比較方便,只要找該 ...
  • 深入理解Java中的不可變對象 不可變對象想必大部分朋友都不陌生,大家在平時寫代碼的過程中100%會使用到不可變對象,比如最常見的String對象、包裝器對象等,那麼到底為何Java語言要這麼設計,真正意圖和考慮點是什麼?可能一些朋友沒有細想過這些問題,今天我們就來聊聊跟不可變對象有關的話題。 以下 ...
  • 提取中間文本(源文本 As String, 前導文本 As String, 結束文本 As String, Optional 移除文本 As String = "", Optional 忽略大小寫 As Boolean = True) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...