C#效率優化(1)-- 使用泛型時避免裝箱

来源:https://www.cnblogs.com/minotauros/archive/2018/11/30/10041644.html
-Advertisement-
Play Games

本想接著上一篇詳解泛型接著寫一篇使用泛型時需要註意的一個性能問題,但是後來想著不如將之前的詳解XX系列更正為現在的效率優化XX系列,記錄在工作時遇到的一些性能優化的經驗和技巧,如果有什麼不足,還請大家多多指出; 在使用集合時,通常為了防止裝箱操作而選擇List<T>、Dictionary<TKey, ...


  本想接著上一篇詳解泛型接著寫一篇使用泛型時需要註意的一個性能問題,但是後來想著不如將之前的詳解XX系列更正為現在的效率優化XX系列,記錄在工作時遇到的一些性能優化的經驗和技巧,如果有什麼不足,還請大家多多指出;

  在使用集合時,通常為了防止裝箱操作而選擇List<T>、Dictionary<TKey, TValue>等泛型集合,但是在使用過程中如果使用不當,依然會產生大量的裝箱操作;

  首先,將值類型的實例當做引用類型來使用時,即會產生裝箱,例如:

int num = 10;
object obj = num;
IEquatable<int> iEquatable = num;

  其次,對於自定義結構,在正常使用時,通常需要註意一些誤裝箱的操作:

public struct MyStruct
{
   public int MyNum;
}

  對該結構MyStruct的實例調用基類Object中的方法時,都會進行裝箱操作,對於靜態方法(Equals、ReferenceEquals)很好理解,對於實例方法,在CLR調用實例方法時,實際上會把調用這個方法的對象當作第一個參數傳入實例方法,而基類Object中的實例方法都會將Object類型的對象作為第一個參數,因此也會發生裝箱,這其中的實例方法包括GetType和虛方法Equals、GetHashCode、ToString;

  其中,GetType方法本身就是通過堆記憶體中與實例數據一起存儲的方法表指針來獲取實例類型信息的,對於值類型實例,本身就沒有這個開銷成員,此處應使用typeof()運算符代替避免裝箱;

  三個虛方法可以通過在MyStruct中重寫來防止裝箱操作;但是對於Equals方法,有一些需要區別註意的地方:

  在調用值類型基類ValueType中的ValueType.Equals(object obj)方法進行比較操作時,會對當前實例和實參obj進行裝箱,共兩次裝箱(抽象基類ValueType依然是類類型);在MyStruct中重寫了該方法MyStruct.Equals(object obj),在調用myStruct1.Equals(myStruct2)時,依然會對myStruct2進行裝箱,共一次裝箱,此時我們可以在MyStruct中聲明一個Equals的重載方法,參數類型同樣為MyStruct,同時對==和!=運算符進行重載:

public struct MyStruct
{
    public int MyNum;
    public override bool Equals(object obj)  //調用時會對實參進行裝箱
    {
        if (!(obj is MyStruct))
        {
            return false;
        }
        MyStruct other = (MyStruct)obj;  //拆箱
        return this.MyNum == other.MyNum;
    }
    public bool Equals(MyStruct other)  //重載Equals方法,避免裝箱
    {
        return this.MyNum == other.MyNum;
    }
    public static bool operator ==(MyStruct left, MyStruct right)  //比較時通常採用==運算符
    {
        return left.Equals(right);
    }
    public static bool operator !=(MyStruct left, MyStruct right)
    {
        return !(left == right);
    }
}

  此時,在調用myStruct1.Equals(myStruct2)、myStruct1 == myStruct2、myStruct1 != myStruct2時都不再產生裝箱操作;

  但是,在使用泛型方法時,例如對於以下的方法,重載方法並不會生效:

static bool MyFunc<T>(T obj1, T obj2)
{
    return obj1.Equals(obj2);
}

  查看其生成的IL代碼可以清楚的知道不生效的原因:

  其中預設對obj2進行了box指令調用,而對於obj1,在調用callvir指令時加入了首碼constrained指令,則會判斷obj1的類型定義中是否存在Equals方法的重寫,如果有則調用重寫方法,如果沒有,則裝箱後調用基類ValueType中的虛方法;前面MyStruct的定義中重寫了Equals方法,因此會調用該重寫方法,此時只觸發一次對obj2的裝箱,但依然不是我們想要的;

  為了避免這個問題,我們需要在MyStruct的定義中實現IEquatable<T>介面,併在這個泛型方法的聲明中添加約束:

public struct MyStruct : IEquatable<MyStruct>
{
    public int MyNum;
    public override bool Equals(object obj)
    {
        if (!(obj is MyStruct))
        {
            return false;
        }
        MyStruct other = (MyStruct)obj;
        return this.MyNum == other.MyNum;
    }
    public bool Equals(MyStruct other)  //實現IEquatable<T>介面中的方法
    {
        return this.MyNum == other.MyNum;
    }
    public static bool operator ==(MyStruct left, MyStruct right)
    {
        return left.Equals(right);
    }
    public static bool operator !=(MyStruct left, MyStruct right)
    {
        return !(left == right);
    }
}
static bool MyFunc<T>(T obj1, T obj2) where T : IEquatable<T>
{
      return obj1.Equals(obj2);
}

  此時,查看其IL代碼,可以發現沒有了box指令,避免了裝箱操作:

  對泛型集合List<Mystruct>使用一些內含比較的實例方法時,也會遇到上面的裝箱問題,解決方法同樣是實現IEquatable<T>介面;以常用的Contains方法舉例:

  List<MyStruct>中的Contains方法中會調用泛型抽象類EqualityComparer<T>.Default的實例來進行比較,而在抽象類EqualityComparer<T>中,會根據類型參數T實例化對應的具體類實例,具體可查看EqualityComparer<T>.CreateComparer()中的實例生成邏輯,其中,會根據T是否實現了IEquatable<T>介面而實例化不同的類的實例:

internal class GenericEqualityComparer<T>: EqualityComparer<T> where T: IEquatable<T>
internal class ObjectEqualityComparer<T>: EqualityComparer<T>

  這兩個類的具體實現這裡不再贅述;

  基於上面的理解,對於值類型,實現基類的虛方法和IEquatable<T>介面對於避免裝箱十分有必要;

 


如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的認可是我寫作的最大動力!

作者:Minotauros
出處:https://www.cnblogs.com/minotauros/

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。


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

-Advertisement-
Play Games
更多相關文章
  • 比如,自定義協議名稱為 myapp,要啟動的本地程式為 myapp.exe。 1)首先向註冊表添加如下內容: 2)用 VS 寫一個本地程式 myapp.exe。我這裡寫的是一個WPF程式: 3)在 web 頁面啟動 myapp.exe 4) 在瀏覽器地址欄錄入 myapp://openAppTest ...
  • AdminLTE 一個基於 bootstrap 的輕量級後臺模板,這個前端界面個人感覺很清爽,對於一個大後端的我來說,可以減少較多的時間去承擔前端的工作但又必須去獨立去完成一個後臺系統開發的任務,並且,文檔還算比較齊全,對著demo可以完成一個基本的前端框架搭建了。大家如有更為好看的又方便後端上手的 ...
  • 搞了一天多,才勉強搞出了一個不緊湊的六邊形統計圖,是真的菜。 這裡ECharts的用法與06說的同一種,直接使用帶all的js 先上個效果圖,用面積來表示人數的多少 1. 參數option的tooltip和title還是一樣設置 2. 還有一個grid,這是用來設置y軸的實際長度的。(我把y軸隱藏了 ...
  • 在C#中 JArray japroimg = new JArray(); strproimg.ToString();這樣會導致tostring之後的字元串中會有大量的空格 使用 japroimg.ToString(Newtonsoft.Json.Formatting.None, null);的fom ...
  • ef開發模式有3種:DateBase First(資料庫優先)、Model First(模型優先)和Code First(代碼優先)。這裡我用的是code first 一個簡單的例子: 簡單介紹一下Database.SetInitializer方法 一:資料庫不存在時重新創建資料庫 Database ...
  • 一、什麼是Dapper? Dapper是一款輕量級Orm框架,它是屬於半自動的,它和Entity Framework和NHibernate不同,它只有一個單文件,沒有很複雜的配置,如果你喜歡原生Sql語句,而且又是喜歡Orm框架,那麼Dapper對於你來說是再適合不過了。 二、Dapper優點我也是 ...
  • .net core執行dotnet ef migrations createmodel等命令出錯 執行dotnet ef migrations createmodel、dotnet ef migrations add initial等命令出錯,報錯信息為:No project was found. ...
  • 事情起源:公司視頻播放一直是採用的嵌入瀏覽器組件實現視頻的預覽回放等功能。這種實現方式要求客戶使用IE瀏覽器。 最近上線項目使用Html 5開發,要求IE11。項目中使用了視頻播放功能,如果全部升級到IE11問題多,工作量大。 存在的主要問題: 有些系統開發較早,不能在IE11上運行。 部分客戶電腦 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...