自定義值類型一定不要忘了重寫Equals,否則性能和空間雙雙堪憂

来源:https://www.cnblogs.com/huangxincheng/archive/2020/05/31/12996361.html

一:背景 1. 講故事 曾今在項目中發現有同事自定義結構體的時候,居然沒有重寫Equals方法,比如下麵這段代碼: static void Main(string[] args) { var list = Enumerable.Range(0, 1000).Select(m => new Point ...


一:背景

1. 講故事

曾今在項目中發現有同事自定義結構體的時候,居然沒有重寫Equals方法,比如下麵這段代碼:


    static void Main(string[] args)
    {
        var list = Enumerable.Range(0, 1000).Select(m => new Point(m, m)).ToList();
        var item = list.FirstOrDefault(m => m.Equals(new Point(int.MaxValue, int.MaxValue)));
        Console.ReadLine();
    }

    public struct Point
    {
        public int x;
        public int y;

        public Point(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }

這代碼貌似也沒啥什麼問題,好像大家平時也是這麼寫,沒關係,有沒有問題,跑一下再用windbg看一下。


0:000> !dumpheap -stat
Statistics:
              MT    Count    TotalSize Class Name
00007ff8826fba20       10        16592 ConsoleApp6.Point[]
00007ff8e0055e70        6        35448 System.Object[]
00007ff8826f5b50     2000        48000 ConsoleApp6.Point

0:000> !dumpheap  -mt 00007ff8826f5b50
         Address               MT     Size
0000020d00006fe0 00007ff8826f5b50       24     

0:000> !do 0000020d00006fe0
Name:        ConsoleApp6.Point
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ff8e00585a0  4000001        8         System.Int32  1 instance                0 x
00007ff8e00585a0  4000002        c         System.Int32  1 instance                0 y

從上面的輸出不知道你看出問題了沒有? 托管堆上居然有2000個Point,而且還可以用 !do 打出來,說明這些都是引用類型。。。這些引用類型哪裡來的? 看代碼應該是 equals 比較時產生的,一次比較就有2個point被裝箱放到托管堆上,這下慘了,,,而且大家應該知道引用對象本身還有(8+8) byte 自帶開銷,這在時間和空間上都是巨大的浪費呀。。。

二: 探究預設的Equals實現

1. 尋找ValueType的Equals實現

為什麼會這樣呢? 我們知道equals是繼承自ValueType的,所以把 ValueType 翻出來看看便知:


    public abstract class ValueType
    {
        public override bool Equals(object obj)
        {
            if (CanCompareBits(this)) {return FastEqualsCheck(this, obj);}
            FieldInfo[] fields = runtimeType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            for (int i = 0; i < fields.Length; i++)
            {
                object obj2 = ((RtFieldInfo)fields[i]).UnsafeGetValue(this);
                object obj3 = ((RtFieldInfo)fields[i]).UnsafeGetValue(obj);
                ...
            }
            return true;
        }
    }

從上面代碼中可以看出有如下三點信息:

<1> 通用的 equals 方法接收object類型,參數裝箱一次。

<2> CanCompareBits,FastEqualsCheck 都是採用object類型,this也需要裝箱一次。

<3> 有兩種比較方式,要麼採用 FastEqualsCheck 比較,要麼採用反射比較,我去.... 反射就玩大了。

綜合來看確實沒毛病, equals 會把比較的兩個對象都進行裝箱。

2. 改進方案

問題找到了,解決起來就簡單了,不走這個通用的 equals 不就行啦,我自定義一個equals方法,然後跑一下代碼。

        public bool Equals(Point other)
        {
            return this.x == other.x && this.y == other.y;
        }

可以看到走了我的自定義的Equals,

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

更多相關文章
  • 女程式員是這麼徵婚的: SELECT * FROM 男人們 WHERE 未婚=true and 同性戀=false and 有房=true and 有車=true and 條件 in (帥氣,紳士,大度,氣質,智慧,溫柔,體貼,會浪漫,活潑,可愛,最好還能帶孩子) and 年齡 between(24 ...
  • API 概述 API(Application Programming Interface),應用程式編程介面。 Java API是一本程式員的 字典 ,是JDK中提供給我們使用的類的說明文檔。 這些類將底層的代碼實現封裝了起來,我們不需要關心這些類是如何實現的,只需要學習這些類如何使用即可。 所以我 ...
  • 0. 前言 該項目使用Maven進行管理和構建,所以需要預先配置好Maven。嗯,在這個系列里就不做過多的介紹了。 1. 創建項目 先創建一個pom.xml 文件,添加以下內容: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http: ...
  • Data StructureThere're two types of variables in C#, reference type and value type.Enum:enum Color{Red=0,Green=1}//equals to enum Color{Red,//start fr... ...
  • 出庫單的功能。能學習了出庫單管理之後,WMS的 主體功能算是完成了。當然一個成熟的WMS還包括了盤點,報表,策略規則,移庫功能及與其他系統(ERP、TMS等)的介面,實現無縫集成,打破信息孤島,讓數據實時、準確和同步。 ...
  • 本文章主要用於介紹在Asp.Net Mvc(C#)中使用Fleck製作一個Html5的即時聊天室,含有完整代碼和演示Demo。 ...
  • 前言 在開發編程中,我們經常會遇到功能非常相似的功能模塊,只是他們的處理的數據不一樣,所以我們會分別採用多個方法來處理不同的數據類型。但是這個時候,我們就會想一個問題,有沒有辦法實現利用同一個方法來傳遞不同種類型的參數呢? 這個時候,泛型也就因運而生,專門來解決這個問題的。 泛型是在C 2.0就推出 ...
  • 最近一個朋友有個關於素數的小東西要寫一下,素數是什麼呢?除了1和他本身不能被其他數整除,那麼這個數就是素數,1除外哦。我們知道概念那就很簡單了,直接代碼擼起。 ...
一周排行
  • 本次課程就正式進入開發部分。 首先我們先搭建項目框架,還是和之前漸進式風格保持一致,除必備組件外,儘量使用原生功能以方便大家理解。 開發工具:vs 2019 或以上 資料庫:SQL SERVER 2017 或以上 其他需要用到的我們在項目過程中再提。 一、新建 MVC項目 1、打開VS 2019,C ...
  • 一.背景說明: 之前分享過一個微服務開發框架, “享一個集成.NET Core+Swagger+Consul+Polly+Ocelot+IdentityServer4+Exceptionless+Apollo+SkyWalking的微服務開發框架”,前兩天在Github上收到一個Issues,是想我 ...
  • 前言 Http我們都已經耳熟能詳了,而關於Http學習的文章網上有很多,各個知識點的講解也可說是深入淺出。然而,學習過後,我們對Http還是一知半解。問題出在了哪? Http是一個客戶機與伺服器之間的通信的協議,真的想學習Http,就必須把客戶機和伺服器也學了,也就是說,必須立體的學習,不然我們永遠 ...
  • 本人製作的這個 “簡易日誌 (SimpleLogger)” 包裡面包含的代碼邏輯,最開始也就是簡單地寫入文本,後來經過實際使用的演化,做了各種優化,添加了一些實用的特性,感覺用著還不錯。正所謂獨樂樂不如眾樂樂,於是將其打包上傳到微軟的包管理庫 NuGet 中,大家可以使用試試,相互交流。 ...
  • 1.需求示意圖 2.需求描述 原本是為了給做unity3d客戶端開發的同事提供不定時的消息推送,比如商城購買道具後服務端將道具信息推送給客戶端。 本篇文章簡化理解,用“相關部門開展活動,向全市人民徵集社會服務改善意見”為例子。但核心想法一致:單向推送(指這個需求上只需要單向)。所以這個功能並不是聊天 ...
  • 找到項目中ServiceStack.Text.dll文件的版本,比如我的版本是5.0.0,到GitHub上下載對應的源碼(https://github.com/ServiceStack/ServiceStack.Text/tags) 打開解決方案,重新生成ServiceStack.Text項目,如果 ...
  • 前言 上一篇文章主要介紹了IL的概念以及基礎的示例代碼,在這一篇文章中我們將通過對象調用看IL。 創建對象與調用方法 class Program { static void Main(string[] args) { var obj = new MyClass(); Console.WriteLin ...
  • abp版本5.9 概述 數據遷移無非就是兩件事情,1、創建資料庫,並根據實體創建對應的表;2、添加一些初始數據 abp的數據遷移也是完成這兩件事,比較特殊的是它是多租戶saas系統,而且支持不同的租戶有獨立的資料庫。所以abp中的遷移要先遷移戶主Host,再遷移租戶Tenant的資料庫 它的遷移定義 ...
  • 本文屬於OData系列 目錄 武裝你的WEBAPI-OData入門 武裝你的WEBAPI-OData便捷查詢 武裝你的WEBAPI-OData分頁查詢 武裝你的WEBAPI-OData資源更新Delta 武裝你的WEBAPI-OData之EDM 武裝你的WEBAPI-OData常見問題 武裝你的WE ...
  • 前言 面試總是會被問到有沒有用過分散式鎖、redis 鎖,大部分讀者平時很少接觸到,所以只能很無奈的回答 “沒有”。本文通過 Spring Boot 整合 redisson 來實現分散式鎖,並結合 demo 測試結果。 首先看下大佬總結的圖 正文 添加依賴 <!--redis--> <depende ...