超碰人人人人人,亚洲AV午夜福利精品一区二区,亚洲欧美综合区丁香五月1区,日韩欧美亚洲系列

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

C#.NET Core 泛型(Generic)的好處和底層原理

freeflydom
2025年1月8日 11:11 本文熱度 332

簡介

泛型參考資料爛大街,基本資料不再贅述,比如泛型接口/委托/方法的使用,逆變與協(xié)變。

泛型好處有如下幾點(diǎn)

  1. 代碼重用
    算法重用,只需要預(yù)先定義好算法,排序,搜索,交換,比較等。任何類型都可以用同一套邏輯
  2. 類型安全
    編譯器保證不會(huì)將int傳給string
  3. 簡單清晰
    減少了類型轉(zhuǎn)換代碼
  4. 性能更強(qiáng)
    減少裝箱/拆箱,泛型算法更優(yōu)異。

為什么說泛型性能更強(qiáng)?

主要在于裝箱帶來的托管堆分配問題以及性能損失

  1. 值類型裝箱會(huì)額外占用內(nèi)存
            var a = new List<int>()
            {
                1,2, 3, 4
            };
            var b = new ArrayList()
            {
                1,2,3,4
            };

變量a:72kb

變量b:184kb

  1. 裝箱/拆箱會(huì)消耗額外的CPU
	public void ArrayTest()
	{
		Stopwatch stopwatch = Stopwatch.StartNew();
		stopwatch.Start();
		ArrayList arrayList = new ArrayList();
		for (int i = 0; i < 10000000; i++)
		{
			arrayList.Add(i);
			_ = (int)arrayList[i];
		}
		stopwatch.Stop();
		Console.WriteLine($"array time is {stopwatch.ElapsedMilliseconds}");
	}
	public void ListTest()
	{
		Stopwatch stopwatch = Stopwatch.StartNew();
		stopwatch.Start();
		List<int> list = new List<int>();
		for (int i = 0; i < 10000000; i++)
		{
			list.Add(i);
			_ = list[i];
		}
		stopwatch.Stop();
		Console.WriteLine($"list time is {stopwatch.ElapsedMilliseconds}");
	}

如此巨大的差異,無疑會(huì)造成GC的管理成本增加以及額外的CPU消耗。

思考一個(gè)問題,如果是引用類型的實(shí)參。差距還會(huì)如此之大嗎?
如果差距不大,那我們使用泛型的理由又是什么呢?

開放/封閉類型

CLR中有多種類型對(duì)象 ,比如引用類型,值類型,接口類型和委托類型,以及泛型類型。

根據(jù)創(chuàng)建行為,他們又被分為開放類型/封閉類型

為什么要說到這個(gè)? 泛型的一個(gè)有優(yōu)點(diǎn)就是代碼復(fù)用,只要定義好算法。剩下的只要往里填就好了。比如List<>開放給任意實(shí)參,大家都可以復(fù)用同一套算法。

舉個(gè)例子

  1. 開放類型是指類型參數(shù)尚未被指定,他們不能被實(shí)例化 List<>,Dictionary<,>,interface 。它們只是搭建好了基礎(chǔ)框架,開放不同的實(shí)參
            Type it = typeof(ITest);
            Activator.CreateInstance(it);//創(chuàng)建失敗
            Type di = typeof(Dictionary<,>);
            Activator.CreateInstance(di);//創(chuàng)建失敗
  1. 封閉類型是指類型已經(jīng)被指定,是可以被實(shí)例化 List<string>,String 就是封閉類型。它們只接受特定含義的實(shí)參
            Type li = typeof(List<string>);
            Activator.CreateInstance(li);//創(chuàng)建成功

代碼爆炸

所以當(dāng)我們使用開放類型時(shí),都會(huì)面臨一個(gè)問題。在JIT編譯階段,CLR會(huì)獲取泛型的IL,再尋找對(duì)應(yīng)的實(shí)參替換,生成合適的本機(jī)代碼。
但這么做有一個(gè)缺點(diǎn),要為每一種不同的泛型類型/方法組合生成,各種各種的本機(jī)代碼。這將明顯增加程序的Assembly,從而損害性能
CLR為了緩解該現(xiàn)象,做了一個(gè)特殊的優(yōu)化:共享方法體

  1. 相同類型實(shí)參,共用一套方法
    如果一個(gè)Assembly中使用了List<Struct>另外一個(gè)Assembly也使用了List<Struct>
    那么CLR只會(huì)生成一套本機(jī)代碼。

  2. 引用類型實(shí)參,共用一套方法
    List<String>與List<Stream> 實(shí)參都是引用類型,它們的值都是托管堆上的指針引用。因此CLR對(duì)指針都可以用同一套方式來操作
    值類型就不行了,比如int與long. 一個(gè)占用4字節(jié),一個(gè)占用8字節(jié)。占用的內(nèi)存不長不一樣,導(dǎo)致無法用同一套邏輯來復(fù)用

眼見為實(shí)1

示例代碼
    internal class Program
    {
        static void Main(string[] args)
        {
            var a = new Test<string>();
            var b = new Test<Stream>();
            
            Debugger.Break();
        }
    }
    public class Test<T>
    {
        public void Add(T value)
        {
		
        }
        public void Remove(T value)
        {
        }
    }

變量a:

變量b

仔細(xì)觀察發(fā)現(xiàn),它們的EEClass完全一致,它們的Add/Remove方法的MethodDesc也完全一直。這印證了上面的說法,引用類型實(shí)參引用同一套方法。

眼見為實(shí)2

點(diǎn)擊查看代碼
    internal class Program
    {
        static void Main(string[] args)
        {
            var a = new Test<int>();
            var b = new Test<long>();
            var c = new Test<MyStruct>();
            
            Debugger.Break();
        }
    }
    public class Test<T>
    {
        public void Add(T value)
        {
        }
        public void Remove(T value)
        {
        }
    }
    public struct MyStruct
    {
        public int Age;
    }

我們?cè)侔岩妙愋蛽Q為值類型,再看看它們的方法表。
變量a:

變量b:

變量c:

一眼就能看出,它們的MethodDesc完全不一樣。這說明在Assembly中。CLR為泛型生成了3套方法。

細(xì)心的朋友可能會(huì)發(fā)現(xiàn),引用類型的實(shí)參變成了一個(gè)叫System.__Canon的類型。CLR 內(nèi)部使用 System.__Canon 來給所有的引用類型做“占位符”使用
有興趣的小伙伴可以參考它的源碼:coreclr\System.Private.CoreLib\src\System__Canon.cs

為什么值類型無法共用同一套方法?

其實(shí)很好理解,引用類型的指針長度是固定的(32位4byte,64位8byte),而值類型的長度不一樣。導(dǎo)致值類型生成的底層匯編無法統(tǒng)一處理。因此值類型無法復(fù)用同一套方法。

眼見為實(shí)

?點(diǎn)擊查看代碼
    internal class Program
    {
        static void Main(string[] args)
        {
            var a = new Test<int>();
            a.Add(1);
            var b = new Test<long>();
            b.Add(1);
            var c = new Test<string>();
            c.Add("");
            var d = new Test<Stream>();
            d.Add(null);
            
            Debugger.Break();
        }
    }
    public class Test<T>
    {
        public void Add(T value)
        {
            var s = value;
        }
        public void Remove(T value)
        {
        }
    }
//變量a
00007FFBAF7B7435  mov         eax,dword ptr [rbp+58h]  
00007FFBAF7B7438  mov         dword ptr [rbp+2Ch],eax    //int 類型步長4 2ch
//變量b
00007FFBAF7B7FD7  mov         rax,qword ptr [rbp+58h]  
00007FFBAF7B7FDB  mov         qword ptr [rbp+28h],rax  //long 類型步長8 28h 匯編不一致
//變量c
00007FFBAF7B8087  mov         rax,qword ptr [rbp+58h]  
00007FFBAF7B808B  mov         qword ptr [rbp+28h],rax  // 28h
//變量d
00007FFBAF7B8087  mov         rax,qword ptr [rbp+58h]  
00007FFBAF7B808B  mov         qword ptr [rbp+28h],rax  // 28h 引用類型地址步長一致,匯編也一致。

泛型的數(shù)學(xué)計(jì)算

在.NET 7之前,如果我們要利用泛型進(jìn)行數(shù)學(xué)運(yùn)算。是無法實(shí)現(xiàn)的。只能通過dynamic來曲線救國

.NET 7中,引入了新的數(shù)學(xué)相關(guān)泛型接口,并提供了接口的默認(rèn)實(shí)現(xiàn)。

https://learn.microsoft.com/zh-cn/dotnet/standard/generics/math

數(shù)學(xué)計(jì)算接口的底層實(shí)現(xiàn)

C#層:
相加的操作主要靠IAdditionOperators接口。

IL層:
+操作符被JIT編譯成了op_Addition抽象方法

對(duì)于int來說,會(huì)調(diào)用int的實(shí)現(xiàn)
System.Int32.System.Numerics.IAdditionOperators

對(duì)于long來說,會(huì)調(diào)用long的實(shí)現(xiàn)
System.Int64.System.Numerics.IAdditionOperators

從原理上來說很簡單,BCL實(shí)現(xiàn)了基本值類型的所有+-*/操作,只要在泛型中做好約束,JIT會(huì)自動(dòng)調(diào)用相應(yīng)的實(shí)現(xiàn)。

結(jié)論

泛型,用就完事了。就是要稍微注意(硬盤比程序員便宜多了)值類型泛型造成的代碼爆炸。

轉(zhuǎn)自https://www.cnblogs.com/lmy5215006/p/18529501


該文章在 2025/1/8 11:11:15 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲(chǔ)管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved