主頁 > .NET開發 > 如何重寫object虛方法

如何重寫object虛方法

2020-09-17 22:44:40 .NET開發

在 C# 中 Object 是所有類的基類,所有的結構和類都直接或間接的派生自它,前面這段話可以說所有的 C# 開發人員都知道,但是我相信其中有一部分程式員并不清楚甚至不知道我們常用的 ToStringEqualsGetHashCode 虛方法都來自于 Object 類,并且我們可以對它們進行重寫,重寫這三個虛方法可以說在專案開發中經常用到,只不過大部分開發人員并未留意這三個虛方法可以重寫,而是自己寫方法來實作,
下面我就來具體講解一下它們三個應該怎么重寫,在這里我需要說明的是本篇文章會大量涉及到設計規范和設計要求,代碼只是作為輔助理解的形式出現,因此文章中的所有代碼將會以代碼段的形式出現,

零、 ToString

ToString 重寫是這三種方法中重寫最簡單的,也是最常用的,但是有一部分開發人員認為重寫 ToString 方法意義不大,那么我在這里要說的是這種想法是錯誤的,當我們在物件上呼叫 ToString 時默認回傳的是類的完全限定名稱,比如說我們在 System.IO.File 物件上呼叫這個方法,就會回傳字串 System.IO.File ,這個結果往往并不是我們所需要的結果并且這個結果也沒有什么意義,例如我們在一個 User 類中重寫 ToString 方法,每次呼叫 User.ToString() 時回傳 "XXX今年XX歲",如果我們不重寫 ToString 方法的話就得不到我們想要的結果,因此我們必須重寫,這時我們就可以這么寫,

public class User
{
    public int Id {get;set;}
    public string Name {get;set;}
    public int Age {get;set;}
    public string Sex {get;set;} 
    public override string ToString()
    {
        return $"{Name}今年{Age}歲!";
    }
}

重寫之后我們就可以得到我們想要的輸出內容了,雖然重寫 ToString 可以得到我們想要的內容,但是我們不能在任何情況下都重寫 ToString, 只有在以下三種情況下方可重寫 ToString :

  1. 代碼面對的最終用戶是開發人員;
  2. 需要寫入日志;
  3. IDE除錯輸出,

在上面三種情況下重寫 ToString 我們還需要遵循一些設計規范,這些設計規范并不是微軟所定義的,而是開發人員在開發程序中總結出來的:

  1. ToString 回傳的字串長度應該簡短,內容描述應該清晰;
  2. 不要從 ToString 方法中回傳 “”,而要回傳 null ;
  3. 不要再 ToString 方法中引發并拋出例外,針對例外應該及時捕獲并處理;
  4. 如果回傳值存在地域文化(比如語言)或存在格式化要求,那么就必須重寫 ToString 方法;
  5. ToString 重寫后必須回傳獨一無二的字串來標識實體物件,

到這里為止我們講解完了 ToString 重寫的方法以及規則,相對來說 ToString 方法重寫是 Object 虛方法重寫中十分簡單的部分,作為開發人員只需按照我前面多說的規則、方法以及實際情況來重寫即可,

一、 Equals 和 ReferenceEquals

在 C# 中如果對兩個物件進行相等判斷,一共有兩種情況分別是:判斷兩者的值相等 或者 判斷兩者的參考地址相同 ,一般情況下我們需要對值型別物件判斷值相等,對參考型別物件判斷指向地址相同,Equals 就是用來對參考型別物件判斷指向地址是否相同的,對于重寫 Equals 方法,很多開發人員認為易如反掌,但是在開發中往往忘記一些很重要的細節,這些細節對于程式來說至關重要,下面我將一一進行詳細講解,

  1. 同一和相等
    所謂的同一指的是兩個物件如果參考的是同一個實體,那么我們就說這兩個物件具有同一性,在 C# 中我們可以利用 object 類或者它的派生類中的 ReferenceEquals 靜態方法來判斷物件之間的同一性,但是同一只是相等的一種,因為在某些情況下兩個物件的部分值或者全部值相等但參考不同,我們也可以說它們具有相等性,下面我們來看一個例子,這個例子通過重寫相等性來實作兩個物件的相等性,

        class Program
     {
         static void Main(string[] args)
         {
             Student s1 = new Student
             {
                 Age = 12,
                 Id = 1,
                 Name = "小明"
             };
             Student s2 = new Student
             {
                 Age = 13,
                 Id = 1,
                 Name = "小明"
             };
             if (Student.ReferenceEquals(s1, s2))
             {
                 Console.WriteLine("是同一個學生");
             }
             else
             {
                 Console.WriteLine("不是同一個學生");
             }
             Console.Read();
         }
     }
    
     class Student
     {
         public int Id { get; set; }
         public string Name { get; set; }
         public int Age { get; set; }
         public static bool ReferenceEquals(Student s1, Student s2)
         {
             if (s1.Equals(s2) ||
                  object.ReferenceEquals(s1, s2) ||
                  s1.Id==s2.Id 
                  s1==s2)
             {
                 return true;
             }
             else
             {
                 return false;
             }
         }
     }
    

    從上述代碼中我們可以看出,雖然 s1 和 s2 參考是不相等的,但是這兩個物件使用了相同的 Id ,因此我們認為 Id 相同的學生就是同一個學生,這么做可以確保資料庫中不會出現重復的錄入,

    Tip:只有參考型別才會可能出現參考相等的情況,對于值型別來說呼叫 ReferenceEquals 方法永遠回傳的是 false ,因為值型別轉換成 object 時是需要裝箱的,即是傳遞的兩個引數是同一個值,也會回傳 false ,

  2. Equals
    判斷兩個物件是否相等,可以使用 Equals ,通過它可以判斷出兩個物件是否具有相同的資料,在 object 中這個方法只是呼叫了 ReferenceEquals 方法來判斷同一性,因此在必要的時候我們必須重寫 Equals 方法,一般來說重寫 Equals 方法常用的步驟如下:

    • 檢查物件是否為 null ;
    • 判斷是否是參考型別,如果是就判斷參考是否相等;
    • 判斷資料型別是否相等;
    • 呼叫具體型別的輔助方法,引數必須是要比較的型別;
    • 判斷哈希碼是否相等,這一步需進行短路操作和欄位比較;
    • 在基類的 Equals 方法被重寫的前提下,必須檢查基類的 Equals 方法;
    • 判斷關鍵欄位的值是否相等;
    • 重寫 GetHashCode 方法;
    • 重寫 == 、 != 運算子,

    Tip: 如果型別是密封型別,那么第三步可以省略掉,

    我們不僅需要按照上述的步驟重寫 Equals 方法,還需要注意如下幾點:

    • GetHashCode 方法不一定回傳的是獨一無二的值,因此我們不能僅僅依賴它的回傳值來判斷兩個物件是否相等;
    • 我們不能在 GetHashCode 和 Equals 中引發任何例外;
    • 必須保證物件之間可以隨意比較,且不能觸發任何例外;
    • 必須實作重寫 Equals 、 GetHashCode 、 == 和 != ,且重寫的演算法必須相同;
    • 盡量不要在可變型別上重寫相等性運算子,

二、 GetHashCode

在上一小節中我們也注意到在重寫 Equals 程序中我們需要重寫 GetHashCode 方法, 所謂 Hash Code 就是用來生成和物件值對應的數字,從而高效的平衡哈希表的作用, 重寫 GetHashCode 方法是比較困難的,下面我就來詳細講解一下重寫規則、方法和注意事項,重寫 GetHashCode 方法需要從性能、安全方面考慮,同時也需要滿足一些要求,

  1. 性能
    由于哈希碼的回傳值是 int 型別,因此會出現部分物件包含的值比 int 取值范圍大的情況,這時哈希碼就肯定會存在重復的情況,所以這時我們要保證哈希碼的回傳值盡可能的唯一,此外針對哈希碼的演算法我們要盡可能的保證回傳的哈希碼應當在 int 型別取值范圍內平均分布,在 Equals 中利用 GetHashCode 方法進行短路操作時我們必須對演算法的性能進行優化,避免將型別作為字典集合中的鍵型別使用,因為這會導致頻繁的呼叫 GetHashCode 方法,在設計 GetHashCode 的演算法時應保證良好的平衡性,即無論哈希表如何對哈希值進行 bucketing,也不會破壞平衡性,一般來說最理想的狀態是兩個物件間 1 bit 的差異應該造成哈希碼 16 bit 的差異,
  2. 安全
    在安全性這方面首先應該遵循的是難以偽造哈希碼物件,一般來說攻擊者會向哈希表中寫入大量哈希值相同的資料,這時如果哈希表實作效率不高將會收到拒絕服務攻擊,我們一般會向來自相關型別的哈希碼使用異或操作,且保證運算元不相近或者相等,如果出現運算元相近或者相等的情況,那么應該考慮使用位移和加法操作,但是多次使用 and 運算子會出現哈希值為 0 的情況,而多次使用 or 運算子則會出現哈希值為 1 的情況,這一點需要注意一下,更進一步的做法是,我們在開發中應該使用移位運算子來分解比 int 大的型別,
  3. 要求
    要求是性能和安全的基礎,只要完全符合了要求的規定,性能和安全才能很好的起作用,要求的第一點也是最基礎的優點,相等的物件它們的哈希碼也相等,其次在特定的生命周期內,特定物件的 GetHashCode 的回傳值始終是一樣的,最后 GetHashCode 不能引發任何例外,如果其中出現例外也必須回傳一個值來表示內部出現例外,

三、總結

本篇文章主要講解了重寫 object 中虛方法的知識,其中涉及到了很多 C# 核心內容,這些內容和知識在實際開發中用的很多,但是大多數開發人員并不在意,因此我希望讀者閱讀完我這篇文章后能對這些內容和知識有初步的了解,

本文由博客一文多發平臺 OpenWrite 發布! 更多文章請掃碼關注公眾號:“喵叔呦” 3Fn2bd.jpg

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/68970.html

標籤:C#

上一篇:使用記憶體映射檔案MMF實作大資料量匯出時的記憶體優化(Linux篇)

下一篇:.NetCore學習筆記:四、AutoMapper物件映射

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more