最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文詳解.NET中GetHashCode方法的正確使用方式

 更新時(shí)間:2026年05月22日 09:03:17   作者:YahirQ  
當(dāng)你看到 Distinct 去重結(jié)果不符合預(yù)期時(shí),很可能不是因?yàn)閿?shù)據(jù)有問題,而是因?yàn)?nbsp;IEqualityComparer 中的 GetHashCode 實(shí)現(xiàn)出錯(cuò)了,本文從一個(gè)真實(shí)代碼片段出發(fā),深入剖析哈希碼的原理、書寫規(guī)范,并給出正確且高性能的實(shí)現(xiàn)方式,需要的朋友可以參考下

一、一段“詭異”的去重代碼

先看下面這段代碼,它嘗試對(duì) hr_data_approve 對(duì)象集合按 userid、begintime、endtime 三個(gè)字段的組合進(jìn)行去重:

public class ApproveDistinctCompare : IEqualityComparer<hr_data_approve>
{
    public bool Equals(hr_data_approve x, hr_data_approve y)
    {
        return x.begintime == y.begintime && x.endtime == y.endtime;
    }
    public int GetHashCode([DisallowNull] hr_data_approve obj)
    {
        return obj.userid.GetHashCode();
    }
}
leaveList = leaveList.Distinct(new ApproveDistinctCompare()).ToList();

乍一看似乎沒什么問題,但實(shí)際運(yùn)行后去重結(jié)果完全不符合預(yù)期:有時(shí)本該去重的記錄留了下來,有時(shí)不該去重的卻被刪掉了。原因就藏在 GetHashCodeEquals不一致上。

二、HashCode是什么?為何如此重要?

2.1 哈希碼的定義

GetHashCode 返回一個(gè) int 類型的數(shù)值,可以理解為對(duì)象的“短指紋”。它的核心作用是為基于哈希表的集合(如 HashSet<T>Dictionary<TKey,TValue>、LINQDistinct() 等)提供快速定位能力。

哈希表的工作原理大致是:

  1. 插入元素時(shí),先調(diào)用 GetHashCode 得到哈希碼,通過 hashCode % bucketCount 決定將元素放入哪個(gè)“桶”。
  2. 查找時(shí),同樣計(jì)算哈希碼,直接定位到對(duì)應(yīng)的桶,然后在桶內(nèi)使用 Equals 逐個(gè)比較。

2.2 哈希碼的黃金法則

如果 Equals(a, b) 返回 true,那么 a.GetHashCode() 必須等于 b.GetHashCode()
反之不要求(不同對(duì)象可以哈希碼相同,這叫“碰撞”)。

違反這一法則,哈希表的行為會(huì)變得完全不可預(yù)測(cè)——因?yàn)閮蓚€(gè)“相等”的對(duì)象可能被分配到不同的桶,導(dǎo)致 Equals 永遠(yuǎn)不會(huì)被調(diào)用,從而誤判為不相等。

三、原代碼錯(cuò)在哪?

方法期望(場(chǎng)景二)原代碼實(shí)現(xiàn)后果
Equals比較 useridbegintime、endtime只比較 begintimeendtime不同用戶只要時(shí)間相同就被誤判為相等
GetHashCode基于三個(gè)字段計(jì)算只基于 userid 計(jì)算相同用戶不同時(shí)間的對(duì)象哈希碼相同,導(dǎo)致大量碰撞,性能下降,且與 Equals 邏輯割裂

更嚴(yán)重的是,由于 EqualsGetHashCode 用的字段完全不同,違反了哈希碼黃金法則:兩個(gè)具有相同 begintime/endtime 但不同 userid 的對(duì)象,Equals 返回 true,而 GetHashCode 因?yàn)?userid 不同而返回不同值。這會(huì)讓 Distinct 內(nèi)部判斷邏輯混亂,去重結(jié)果隨機(jī)錯(cuò)誤。

四、正確的GetHashCode應(yīng)該怎么寫?

針對(duì)場(chǎng)景二(基于 userid、begintime、endtime 三者組合去重),正確的實(shí)現(xiàn)如下:

public class ApproveDistinctCompare : IEqualityComparer<hr_data_approve>
{
    public bool Equals(hr_data_approve x, hr_data_approve y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (x is null || y is null) return false;
        return x.userid == y.userid 
            && x.begintime == y.begintime 
            && x.endtime == y.endtime;
    }
    public int GetHashCode([DisallowNull] hr_data_approve obj)
    {
        if (obj is null) throw new ArgumentNullException(nameof(obj));
        unchecked
        {
            int hash = 17;
            hash = hash * 31 + (obj.userid?.GetHashCode() ?? 0);
            hash = hash * 31 + (obj.begintime?.GetHashCode() ?? 0);
            hash = hash * 31 + (obj.endtime?.GetHashCode() ?? 0);
            return hash;
        }
    }
}

4.1 代碼逐行解讀

unchecked關(guān)鍵字

  • 哈希計(jì)算涉及乘法,int 很容易溢出。unchecked 允許溢出時(shí)自動(dòng)回繞(wrap around),這是哈希算法的正常現(xiàn)象,無需拋出異常。

為什么選17和31?

  • 這兩個(gè)數(shù)是素?cái)?shù),使用素?cái)?shù)可以降低哈希碰撞的概率。
  • 31 是經(jīng)典的乘數(shù)(Java 的 Objects.hash() 也用 31),因?yàn)?31 * i 可被 JIT 優(yōu)化為 (i << 5) - i,位運(yùn)算效率高。

為什么每次都乘以31?

  • 避免不同字段順序產(chǎn)生相同的哈希值。比如 ("A","B")("B","A") 如果僅累加,會(huì)得到相同結(jié)果;而乘以質(zhì)數(shù)再加新字段,可以讓順序影響最終哈希值。

obj.userid?.GetHashCode() ?? 0

  • 處理字段可能為 null 的情況:當(dāng) useridnull 時(shí),?. 阻止調(diào)用 GetHashCode,表達(dá)式返回 null,?? 0 將其替換為 0。這樣既安全又不會(huì)拋空引用異常。

為什么要用三個(gè)字段?

  • 必須與 Equals 基于完全相同的字段組合。因?yàn)?Equals 判斷三個(gè)字段全部相等才返回 true,所以只有當(dāng)三個(gè)字段都相同時(shí),哈希碼也必須相同。這是契約的要求。

4.2 簡(jiǎn)化寫法(.NET Core 2.1+)

如果你使用的框架版本支持 System.HashCode,可以大幅簡(jiǎn)化:

public override int GetHashCode() => HashCode.Combine(userid, begintime, endtime);

VSCode / Visual Studio 還提供了自動(dòng)生成 EqualsGetHashCode 的功能(右鍵 → 快速操作 → 生成 Equals/GetHashCode),非常推薦使用。

五、常見誤區(qū)與最佳實(shí)踐

誤區(qū)1:只在Equals里寫邏輯,GetHashCode隨便返回一個(gè)常數(shù)

  • 后果:所有對(duì)象哈希碼相同,全部落入同一個(gè)桶,哈希表退化成鏈表,性能從 O(1) 變成 O(n)。

誤區(qū)2:用可變字段參與哈希碼計(jì)算

  • 后果:對(duì)象加入 HashSet 后,如果參與哈希碼的字段被修改,該對(duì)象在哈希表內(nèi)的位置就會(huì)“丟失”,再也無法被查找或刪除。推薦僅用不可變字段(如 Id、創(chuàng)建時(shí)間)計(jì)算哈希碼。

誤區(qū)3:不處理null值

  • 后果:當(dāng)字段為 null 時(shí)調(diào)用其 GetHashCode 會(huì)拋出 NullReferenceException。始終使用 ?.GetHashCode() ?? 0 或顯式判斷。

最佳實(shí)踐總結(jié)

  1. EqualsGetHashCode 必須基于完全相同的字段組合。
  2. 使用質(zhì)數(shù)(如 17、31)作為初始值和乘數(shù),降低碰撞率。
  3. unchecked 處理溢出。
  4. 處理可能為 null 的字段。
  5. 優(yōu)先使用 HashCode.Combine 或 IDE 生成工具。
  6. 哈希碼計(jì)算中使用的字段應(yīng)為只讀(或至少不應(yīng)在對(duì)象作為哈希表鍵時(shí)被修改)。

六、結(jié)語

GetHashCode 看起來只是簡(jiǎn)單的整數(shù)計(jì)算,但它與 Equals 共同構(gòu)成了 .NET 中所有哈希集合的基石。一個(gè)小小的不一致,就可能讓 Distinct、HashSetDictionary 出現(xiàn)匪夷所思的錯(cuò)誤。下次再遇到“明明數(shù)據(jù)一樣,為什么去重?zé)o效”的問題,請(qǐng)第一時(shí)間檢查 GetHashCodeEquals 是否“言行一致”。

記住黃金法則

// 如果以下代碼輸出 true,那么 hash1 必須等于 hash2
bool equal = comparer.Equals(a, b);
int hash1 = comparer.GetHashCode(a);
int hash2 = comparer.GetHashCode(b);

希望這篇文章能幫你徹底理解哈希碼,寫出健壯、高效的自定義比較器。

以上就是一文詳解.NET中GetHashCode方法的正確使用方式的詳細(xì)內(nèi)容,更多關(guān)于.NET GetHashCode正確使用方式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • .NET事件監(jiān)聽機(jī)制的局限與擴(kuò)展分析

    .NET事件監(jiān)聽機(jī)制的局限與擴(kuò)展分析

    這篇文章主要介紹了.NET事件監(jiān)聽機(jī)制的局限與擴(kuò)展,詳細(xì)分析了.NET事件監(jiān)聽機(jī)制的機(jī)制與優(yōu)劣,有助于更好的理解.NET的運(yùn)行原理,需要的朋友可以參考下
    2014-11-11
  • asp.net UrlReWriter使用經(jīng)驗(yàn)小結(jié)

    asp.net UrlReWriter使用經(jīng)驗(yàn)小結(jié)

    UrlRewriter 是微軟封裝好了的一個(gè)URL重寫組件。使用它可以讓我節(jié)約很多自已開發(fā)的時(shí)間。 好了,開始講述我的應(yīng)用經(jīng)驗(yàn),這只是很菜鳥的經(jīng)驗(yàn),高手就不用看了。
    2008-11-11
  • ASP.NET?Core?MVC緩存Tag?Helpers到內(nèi)存

    ASP.NET?Core?MVC緩存Tag?Helpers到內(nèi)存

    這篇文章介紹了ASP.NET?Core?MVC緩存Tag?Helpers到內(nèi)存的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-02-02
  • 淺談ASP.NET Core靜態(tài)文件處理源碼探究

    淺談ASP.NET Core靜態(tài)文件處理源碼探究

    這篇文章主要介紹了淺談ASP.NET Core靜態(tài)文件處理源碼探究,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • VS2015中C#版本6.0的新特性 你需要知道

    VS2015中C#版本6.0的新特性 你需要知道

    VS2015內(nèi)置的C#版本為6.0,提供了一些新特性,這篇文章主要為大家詳細(xì)介紹了VS2015中C#版本6.0的8種新特性,感興趣的小伙伴們可以參考一下
    2016-06-06
  • .Net使用SuperSocket框架實(shí)現(xiàn)WebSocket后端

    .Net使用SuperSocket框架實(shí)現(xiàn)WebSocket后端

    這篇文章介紹了.Net使用SuperSocket框架實(shí)現(xiàn)WebSocket后端,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-01-01
  • ASP.NET Core中ResourceFilter過濾器的實(shí)現(xiàn)

    ASP.NET Core中ResourceFilter過濾器的實(shí)現(xiàn)

    ASP.NET Core 中的?ResourceFilter?是一種非常有用的過濾器類型,允許開發(fā)人員在請(qǐng)求到達(dá)控制器操作方法之前或響應(yīng)返回之前執(zhí)行一些操作,下面就來介紹一下,感興趣的可以了解一下
    2025-05-05
  • MVC4制作網(wǎng)站教程第三章 瀏覽用戶組操作3.1

    MVC4制作網(wǎng)站教程第三章 瀏覽用戶組操作3.1

    這篇文章主要為大家詳細(xì)介紹了MVC4制作網(wǎng)站教程,瀏覽用戶組功能的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-08-08
  • 解決.net項(xiàng)目中上傳的圖片或者文件太大無法上傳問題

    解決.net項(xiàng)目中上傳的圖片或者文件太大無法上傳問題

    本文主要介紹了解決.net項(xiàng)目中上傳的圖片或者文件太大無法上傳問題的具有方法,具有很好的參考價(jià)值,有需要的朋友可以看下
    2016-12-12
  • asp.net 網(wǎng)絡(luò)硬盤實(shí)現(xiàn)分析

    asp.net 網(wǎng)絡(luò)硬盤實(shí)現(xiàn)分析

    隨著網(wǎng)絡(luò)技術(shù)的日益普及和信息化建設(shè)的重視,網(wǎng)絡(luò)硬盤作為一種新型安全的網(wǎng)絡(luò)存儲(chǔ)系統(tǒng),已越來越受到人們的重視和喜歡。
    2011-02-02

最新評(píng)論

开鲁县| 固原市| 志丹县| 黔江区| 灵山县| 东城区| 咸丰县| 云和县| 华坪县| 尚义县| 长垣县| 会泽县| 镇赉县| 邛崃市| 古浪县| 雷山县| 菏泽市| 南部县| 库尔勒市| 安福县| 观塘区| 简阳市| 赞皇县| 长丰县| 唐河县| 类乌齐县| 岳池县| 吉木乃县| 沂源县| 清镇市| 忻城县| 博罗县| 阳东县| 蒙山县| 济源市| 如皋市| 高尔夫| 巧家县| 广水市| 衡阳市| 本溪市|