成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

C# 中的 ref 已經(jīng)被放開(kāi),或許你已經(jīng)不認(rèn)識(shí)了

開(kāi)發(fā) 前端
不知道大家有沒(méi)有發(fā)現(xiàn),在 C# 7.0 之后,語(yǔ)言團(tuán)隊(duì)對(duì)性能這一塊真的是前所未有的重視,還專門(mén)為此出了各種類和底層支持,比如說(shuō) Span, Memory,ValueTask,還有本篇要介紹的ref。

一:背景

1. 講故事

最近在翻 netcore 源碼看,發(fā)現(xiàn)框架中有不少的代碼都被 ref 給修飾了,我去,這還是我認(rèn)識(shí)的 ref 嗎?就拿 Span 來(lái)說(shuō),代碼如下:

public readonly ref struct Span<T>
    {
        public ref T GetPinnableReference()
        {
            ref T result = ref Unsafe.AsRef<T>(null);
            if (_length != 0)
            {
                result = ref _pointer.Value;
            }
            return ref result;
        }

        public ref T this[int index]
        {
            get
            {
                return ref Unsafe.Add(ref _pointer.Value, index);
            }
        }             
    }

是不是到處都有 ref,在 struct 上有,在 local variable 也有,在 方法簽名處 也有,在 方法調(diào)用處 也有,在 屬性 上也有, 在 return處 也有,簡(jiǎn)直是應(yīng)有盡有,太????啦,那這一篇我們就來(lái)聊聊這個(gè)奇葩的 ref。

二:ref 各場(chǎng)景下的代碼解析

1. 動(dòng)機(jī)

不知道大家有沒(méi)有發(fā)現(xiàn),在 C# 7.0 之后,語(yǔ)言團(tuán)隊(duì)對(duì)性能這一塊真的是前所未有的重視,還專門(mén)為此出了各種類和底層支持,比如說(shuō) Span, Memory,ValueTask,還有本篇要介紹的ref。

在大家傳統(tǒng)的認(rèn)知中 ref 是用在方法參數(shù)上,用于給 值類型 做引用傳值,一個(gè)是為了大家業(yè)務(wù)上需要多次原地修改的情況,二個(gè)是為了避免值類型的copy引發(fā)的性能開(kāi)銷(xiāo),不知道是哪一位大神腦洞大開(kāi),將 ref 應(yīng)用在你所知道的代碼各處,最終目的都是盡可能的提升性能。

2. ref struct 分析

從小就被教育 值類型分配在棧上,引用類型是在堆上,這話也是有問(wèn)題的,因?yàn)橹殿愋鸵部梢苑峙湓诙焉希热缦旅娲a的 Location。

public class Program
    {
        public static void Main(string[] args)
        {
            var person = new Person() { Name = "張三", Location = new Point() { X = 10, Y = 20 } };

            Console.ReadLine();
        }
    }

    public class Person
    {
        public string Name { get; set; }

        public Point Location { get; set; }  //分配在堆上
    }

    public struct Point
    {
        public int X { get; set; }
        public int Y { get; set; }
    }

其實(shí)這也是很多新手朋友學(xué)習(xí)值類型疑惑的地方,可以用 windbg 到托管堆找一下 Person 問(wèn)問(wèn)看,如下代碼:

0:000> !dumpheap -type Person
         Address               MT     Size
0000010e368aadb8 00007ffaf50c2340       32     

0:000> !do 0000010e368aadb8
Name:        ConsoleApp2.Person
MethodTable: 00007ffaf50c2340
EEClass:     00007ffaf50bc5e8
Size:        32(0x20) bytes
File:        E:\net5\ConsoleApp1\ConsoleApp2\bin\Debug\netcoreapp3.1\ConsoleApp2.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ffaf5081e18  4000001        8        System.String  0 instance 0000010e368aad98 <Name>k__BackingField
00007ffaf50c22b0  4000002       10    ConsoleApp2.Point  1 instance 0000010e368aadc8 <Location>k__BackingField

0:000> dp 0000010e368aadc8
0000010e`368aadc8  00000014`0000000a 00000000`00000000

上面代碼最后一行 00000014`0000000a 中的 14 和 a 就是 y 和 x 的值,穩(wěn)穩(wěn)當(dāng)當(dāng)?shù)拇娣旁诙阎校绻氵€不信就看看 gc 0代堆的范圍。

0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000010E368A1030
generation 1 starts at 0x0000010E368A1018
generation 2 starts at 0x0000010E368A1000
ephemeral segment allocation context: none
         segment             begin         allocated              size
0000010E368A0000  0000010E368A1000  0000010E368B55F8  0x145f8(83448)

從最后一行可看出,剛才的  0000010e368aadc8 確實(shí)是在 0 代堆 0x0000010E368A1030 - 0000010E368B55F8 的范圍內(nèi)。

接下來(lái)的問(wèn)題就是能不能給 struct 做一個(gè)限制,就像泛型約束一樣,不準(zhǔn) struct 分配在堆上,有沒(méi)有辦法呢?辦法就是加一個(gè) ref 限定即可,如下圖:

圖片圖片

從錯(cuò)誤提示中可以看出,有意讓 struct 分配到堆上的操作都是嚴(yán)格禁止的,要想過(guò)編譯器只能將 class person 改成 ref struct person,也就是文章開(kāi)頭 Span  和  this[int index] 這樣,動(dòng)機(jī)可想而知,一切都是為了性能。

3. ref method 分析

給方法的參數(shù)傳引用地址,我想很多朋友都已經(jīng)輕車(chē)熟路了,比如下面這樣:

public static int GetNum(ref int i)
        {
            return i;
        }

現(xiàn)在大家可以試著跳出思維定勢(shì),既然可以往方法內(nèi)仍 引用地址 ,那能不能往方法外拋 引用地址 呢?如果這也能實(shí)現(xiàn)就比較有意思了,我可以對(duì)集合內(nèi)的某一些數(shù)據(jù)進(jìn)行引用地址返回,在方法外照樣可以修改這些返回值,畢竟傳來(lái)傳去都是引用地址,如下代碼所示:

public class Program
    {
        public static void Main(string[] args)
        {
            var nums = new int[3] { 10, 20, 30 };

            ref int num = ref GetNum(nums);

            num = 50;

            Console.WriteLine($"nums= {string.Join(",",nums)}");

            Console.ReadLine();
        }

        public static ref int GetNum(int[] nums)
        {
            return ref nums[2];
        }
    }

圖片圖片

可以看到,數(shù)組的最后一個(gè)值已經(jīng)由 30 -> 50 了,有些朋友可能會(huì)比較驚訝,這到底是怎么玩的,不用想就是引用地址到處漂,不信的話,看看 IL 代碼咯。

.method public hidebysig static 
 int32& GetNums (
  int32[] nums
 ) cil managed 
{
 // Method begins at RVA 0x209c
 // Code size 13 (0xd)
 .maxstack 2
 .locals init (
  [0] int32&
 )

 // {
 IL_0000: nop
 // return ref nums[2];
 IL_0001: ldarg.0
 IL_0002: ldc.i4.2
 IL_0003: ldelema [System.Runtime]System.Int32
 IL_0008: stloc.0
 // (no C# code)
 IL_0009: br.s IL_000b

 IL_000b: ldloc.0
 IL_000c: ret
} // end of method Program::GetNums

.method public hidebysig static 
 void Main (
  string[] args
 ) cil managed 
{
 IL_0013: ldloc.0
 IL_0014: call int32& ConsoleApp2.Program::GetNums(int32[])
 IL_0019: stloc.1
 IL_001a: ldloc.1
 IL_001b: ldc.i4.s 50
 IL_003e: pop
 IL_003f: ret
} // end of method Program::Main

可以看到,到處都是 & 取值運(yùn)算符,更直觀一點(diǎn)的話用 windbg 看一下。

0:000> !clrstack -a
OS Thread Id: 0x7040 (0)
000000D4E777E760 00007FFAF1C5108F ConsoleApp2.Program.Main(System.String[]) [E:\net5\ConsoleApp1\ConsoleApp2\Program.cs @ 28]
    PARAMETERS:
        args (0x000000D4E777E7F0) = 0x00000218c9ae9e60
    LOCALS:
        0x000000D4E777E7C8 = 0x00000218c9aeadd8
        0x000000D4E777E7C0 = 0x00000218c9aeadf0

0:000> dp 0x00000218c9aeadf0
00000218`c9aeadf0  00000000`00000032 00000000`00000000

上面代碼處的 0x00000218c9aeadf0 就是 num 的引用地址,繼續(xù)用 dp 看一下這個(gè)地址上的值為 16進(jìn)制的32,也就是十進(jìn)制的 50 哈。

三:總結(jié)

總的來(lái)說(shuō),netcore 就是在當(dāng)初盛行的 云計(jì)算 和 虛擬化 時(shí)代誕生,基因和使命促使它必須要優(yōu)化優(yōu)化再優(yōu)化,再小的螞蟻也是肉,最后就是 C# 大法 ????

責(zé)任編輯:武曉燕 來(lái)源: 一線碼農(nóng)聊技術(shù)
相關(guān)推薦

2024-02-01 08:29:42

大數(shù)據(jù)計(jì)算業(yè)務(wù)

2014-11-19 15:08:09

CSS

2021-05-10 08:34:37

USB接口USB網(wǎng)絡(luò)設(shè)備

2019-04-09 10:45:18

IPv6運(yùn)營(yíng)商協(xié)議

2024-06-17 08:16:22

2020-01-07 11:04:02

JavaJSP前端

2013-11-29 10:23:29

OS X 10.9問(wèn)題蘋(píng)果

2009-12-21 09:28:21

AMD CEO英特爾CEO

2017-07-28 09:39:12

隱式IntentAndroid M

2014-03-11 10:03:25

設(shè)計(jì)模式

2018-08-03 16:41:23

數(shù)據(jù)房子存量

2009-04-17 15:19:25

職場(chǎng)過(guò)時(shí)晚年

2023-03-31 07:49:52

NVIDIAGPU芯片

2013-07-11 16:10:39

蘋(píng)果時(shí)代

2024-10-10 12:05:03

2011-02-25 17:00:25

SQL閏年

2025-06-11 03:15:00

2022-09-15 14:04:07

Go語(yǔ)言泛型

2015-08-11 09:51:06

谷歌重組Alphabet
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产成人区 | 在线色网| 久久久精品视 | 国产在线一区二区 | 成人免费在线 | 亚洲成人福利视频 | 亚洲精品中文字幕在线 | 精品视频一区二区三区在线观看 | 欧洲一级视频 | 少妇精品久久久久久久久久 | 午夜欧美一区二区三区在线播放 | 色婷婷一区二区三区四区 | 国产9 9在线 | 中文 | 亚洲成人精品 | 一级黄色播放 | 99爱在线免费观看 | 全免一级毛片 | 性高湖久久久久久久久aaaaa | 国产成都精品91一区二区三 | 日韩视频精品在线 | 91 在线| 国产一区二区三区久久久久久久久 | 国产精品国产成人国产三级 | 色欧美片视频在线观看 | 人人射人人插 | 999久久久国产精品 欧美成人h版在线观看 | 老外几下就让我高潮了 | 99久久久国产精品免费消防器 | 国产一区二区三区在线 | 久久久亚洲成人 | 国产精品自产拍 | 亚洲一区国产 | 色香婷婷 | 久久久精彩视频 | 亚洲精品日韩在线 | 国产激情在线播放 | 欧美日韩国产中文 | 黄色欧美在线 | 午夜成人免费视频 | 亚洲欧美综合精品久久成人 | 国产日韩一区二区 |