Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 91高清完整版在线观看,成人欧美影片在线观看免费视频,亚洲情侣自拍

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          JavaScript函數(shù)式編程指南(一)

          說到函數(shù)式編程,大家可能第一印象都是學(xué)院派的那些晦澀難懂的代碼,充滿了一大堆抽象的不知所云的符號(hào),似乎只有大學(xué)里的計(jì)算機(jī)教授才會(huì)使用這些東西。在曾經(jīng)的某個(gè)時(shí)代可能確實(shí)如此,但是近年來(lái)隨著技術(shù)的發(fā)展,函數(shù)式編程已經(jīng)在實(shí)際生產(chǎn)中發(fā)揮巨大的作用了,越來(lái)越多的語(yǔ)言開始加入閉包,匿名函數(shù)等非常典型的函數(shù)式編程的特性,從某種程度上來(lái)講,函數(shù)式編程正在逐步“同化”命令式編程。

          JavaScript 作為一種典型的多范式編程語(yǔ)言,這兩年隨著React的火熱,函數(shù)式編程的概念也開始流行起來(lái),RxJS、cycleJS、lodashJS、underscoreJS等多種開源庫(kù)都使用了函數(shù)式的特性。所以下面介紹一些函數(shù)式編程的知識(shí)和概念。

          純函數(shù)

          如果你還記得一些初中的數(shù)學(xué)知識(shí)的話,函數(shù) f 的概念就是,對(duì)于輸入 x 產(chǎn)生一個(gè)輸出 y = f(x)。這便是一種最簡(jiǎn)單的純函數(shù)。純函數(shù)的定義是,對(duì)于相同的輸入,永遠(yuǎn)會(huì)得到相同的輸出,而且沒有任何可觀察的副作用,也不依賴外部環(huán)境的狀態(tài)。

          下面來(lái)舉個(gè)栗子,比如在Javascript中對(duì)于數(shù)組的操作,有些是純的,有些就不是純的:

          在函數(shù)式編程中,我們想要的是 slice 這樣的純函數(shù),而不是 splice這種每次調(diào)用后都會(huì)把數(shù)據(jù)弄得一團(tuán)亂的函數(shù)。

          為什么函數(shù)式編程會(huì)排斥不純的函數(shù)呢?下面再看一個(gè)例子:

          //不純的
          var min = 18;
          var checkage = age => age > min;
          //純的,這很函數(shù)式
          var checkage = age => age > 18;
          

          在不純的版本中,checkage 這個(gè)函數(shù)的行為不僅取決于輸入的參數(shù) age,還取決于一個(gè)外部的變量 min,換句話說,這個(gè)函數(shù)的行為需要由外部的系統(tǒng)環(huán)境決定。對(duì)于大型系統(tǒng)來(lái)說,這種對(duì)于外部狀態(tài)的依賴是造成系統(tǒng)復(fù)雜性大大提高的主要原因。

          可以注意到,純的 checkage 把關(guān)鍵數(shù)字 18 硬編碼在函數(shù)內(nèi)部,擴(kuò)展性比較差,我們可以在后面的柯里化中看到如何用優(yōu)雅的函數(shù)式解決這種問題。

          純函數(shù)不僅可以有效降低系統(tǒng)的復(fù)雜度,還有很多很棒的特性,比如可緩存性:

          import _ from 'lodash';
          var sin = _.memorize(x => Math.sin(x));
          //第一次計(jì)算的時(shí)候會(huì)稍慢一點(diǎn)
          var a = sin(1);
          //第二次有了緩存,速度極快
          var b = sin(1);
          

          函數(shù)的柯里化

          函數(shù)柯里化(curry)的定義很簡(jiǎn)單:傳遞給函數(shù)一部分參數(shù)來(lái)調(diào)用它,讓它返回一個(gè)函數(shù)去處理剩下的參數(shù)。

          比如對(duì)于加法函數(shù) var add = (x, y) => x + y ,我們可以這樣進(jìn)行柯里化:

          //比較容易讀懂的ES5寫法
          var add = function(x){
           return function(y){
           return x + y
           }
          }
          //ES6寫法,也是比較正統(tǒng)的函數(shù)式寫法
          var add = x => (y => x + y);
          //試試看
          var add2 = add(2);
          var add200 = add(200);
          add2(2); // =>4
          add200(50); // =>250
          

          對(duì)于加法這種極其簡(jiǎn)單的函數(shù)來(lái)說,柯里化并沒有什么大用處。

          還記得上面那個(gè) checkage 的函數(shù)嗎?我們可以這樣柯里化它:

          var checkage = min => (age => age > min);
          var checkage18 = checkage(18);
          checkage18(20);
          // =>true
          

          事實(shí)上柯里化是一種“預(yù)加載”函數(shù)的方法,通過傳遞較少的參數(shù),得到一個(gè)已經(jīng)記住了這些參數(shù)的新函數(shù),某種意義上講,這是一種對(duì)參數(shù)的“緩存”,是一種非常高效的編寫函數(shù)的方法:

          函數(shù)組合

          學(xué)會(huì)了使用純函數(shù)以及如何把它柯里化之后,我們會(huì)很容易寫出這樣的“包菜式”代碼:

          h(g(f(x)));
          

          雖然這也是函數(shù)式的代碼,但它依然存在某種意義上的“不優(yōu)雅”。為了解決函數(shù)嵌套的問題,我們需要用到“函數(shù)組合”:

          //兩個(gè)函數(shù)的組合
          var compose = function(f, g) {
           return function(x) {
           return f(g(x));
           };
          };
          //或者
          var compose = (f, g) => (x => f(g(x)));
          var add1 = x => x + 1;
          var mul5 = x => x * 5;
          compose(mul5, add1)(2);
          // =>15 
          

          我們定義的compose就像雙面膠一樣,可以把任何兩個(gè)純函數(shù)結(jié)合到一起。當(dāng)然你也可以擴(kuò)展出組合三個(gè)函數(shù)的“三面膠”,甚至“四面膠”“N面膠”。

          這種靈活的組合可以讓我們像拼積木一樣來(lái)組合函數(shù)式的代碼:

          var first = arr => arr[0];
          var reverse = arr => arr.reverse();
          var last = compose(first, reverse);
          last([1,2,3,4,5]);
          // =>5
          

          Point Free

          有了柯里化和函數(shù)組合的基礎(chǔ)知識(shí),下面介紹一下Point Free這種代碼風(fēng)格。

          細(xì)心的話你可能會(huì)注意到,之前的代碼中我們總是喜歡把一些對(duì)象自帶的方法轉(zhuǎn)化成純函數(shù):

          var map = (f, arr) => arr.map(f);
          var toUpperCase = word => word.toUpperCase();
          

          這種做法是有原因的。

          Point Free這種模式現(xiàn)在還暫且沒有中文的翻譯,有興趣的話可以看看這里的英文解釋:

          https://en.wikipedia.org/wiki/Tacit_programming

          用中文解釋的話大概就是,不要命名轉(zhuǎn)瞬即逝的中間變量,比如:

          //這不Piont free
          var f = str => str.toUpperCase().split(' ');
          

          這個(gè)函數(shù)中,我們使用了 str 作為我們的中間變量,但這個(gè)中間變量除了讓代碼變得長(zhǎng)了一點(diǎn)以外是毫無(wú)意義的。下面改造一下這段代碼:

          var toUpperCase = word => word.toUpperCase();
          var split = x => (str => str.split(x));
          var f = compose(split(' '), toUpperCase);
          f("abcd efgh");
          // =>["ABCD", "EFGH"]
          

          這種風(fēng)格能夠幫助我們減少不必要的命名,讓代碼保持簡(jiǎn)潔和通用。當(dāng)然,為了在一些函數(shù)中寫出Point Free的風(fēng)格,在代碼的其它地方必然是不那么Point Free的,這個(gè)地方需要自己取舍。

          聲明式與命令式代碼

          命令式代碼的意思就是,我們通過編寫一條又一條指令去讓計(jì)算機(jī)執(zhí)行一些動(dòng)作,這其中一般都會(huì)涉及到很多繁雜的細(xì)節(jié)。

          而聲明式就要優(yōu)雅很多了,我們通過寫表達(dá)式的方式來(lái)聲明我們想干什么,而不是通過一步一步的指示。

          //命令式
          var CEOs = [];
          for(var i = 0; i < companies.length; i++){
           CEOs.push(companies[i].CEO)
          }
          //聲明式
          var CEOs = companies.map(c => c.CEO);
          

          命令式的寫法要先實(shí)例化一個(gè)數(shù)組,然后再對(duì) companies 數(shù)組進(jìn)行for循環(huán)遍歷,手動(dòng)命名、判斷、增加計(jì)數(shù)器,就好像你開了一輛零件全部暴露在外的汽車一樣,雖然很機(jī)械朋克風(fēng),但這并不是優(yōu)雅的程序員應(yīng)該做的。

          聲明式的寫法是一個(gè)表達(dá)式,如何進(jìn)行計(jì)數(shù)器迭代,返回的數(shù)組如何收集,這些細(xì)節(jié)都隱藏了起來(lái)。它指明的是做什么,而不是怎么做。除了更加清晰和簡(jiǎn)潔之外,map 函數(shù)還可以進(jìn)一步獨(dú)立優(yōu)化,甚至用解釋器內(nèi)置的速度極快的 map 函數(shù),這么一來(lái)我們主要的業(yè)務(wù)代碼就無(wú)須改動(dòng)了。

          函數(shù)式編程的一個(gè)明顯的好處就是這種聲明式的代碼,對(duì)于無(wú)副作用的純函數(shù),我們完全可以不考慮函數(shù)內(nèi)部是如何實(shí)現(xiàn)的,專注于編寫業(yè)務(wù)代碼。優(yōu)化代碼時(shí),目光只需要集中在這些穩(wěn)定堅(jiān)固的函數(shù)內(nèi)部即可。

          相反,不純的不函數(shù)式的代碼會(huì)產(chǎn)生副作用或者依賴外部系統(tǒng)環(huán)境,使用它們的時(shí)候總是要考慮這些不干凈的副作用。在復(fù)雜的系統(tǒng)中,這對(duì)于程序員的心智來(lái)說是極大的負(fù)擔(dān)。

          尾聲

          任何代碼都是要有實(shí)際用處才有意義,對(duì)于JS來(lái)說也是如此。然而現(xiàn)實(shí)的編程世界顯然不如范例中的函數(shù)式世界那么美好,實(shí)際應(yīng)用中的JS是要接觸到ajax、DOM操作,NodeJS環(huán)境中讀寫文件、網(wǎng)絡(luò)操作這些對(duì)于外部環(huán)境強(qiáng)依賴,有明顯副作用的“很臟”的工作。

          參考

          https://zhuanlan.zhihu.com/p/21714695

          文共6287字,預(yù)計(jì)學(xué)習(xí)時(shí)長(zhǎng)20分鐘或更長(zhǎng)


          圖片來(lái)源:Irvan Smith / Unsplash


          人們認(rèn)為JavaScript是最適合初學(xué)者的語(yǔ)言。一部分原因在于JavaScript在互聯(lián)網(wǎng)中運(yùn)用廣泛,另一部分原因在于其自身特性使得即使編寫的代碼不那么完美依然可以運(yùn)行:無(wú)論是否少了一個(gè)分號(hào)或是內(nèi)存管理問題,它都不像許多其他語(yǔ)言那樣嚴(yán)格,但在開始學(xué)習(xí)之前,要確保你已經(jīng)知道JavaScript的來(lái)龍去脈,包括可以自動(dòng)完成的事情和“幕后”的操作。


          本文將介紹一些面試時(shí)關(guān)于JavaScript的常見問題,以及一些突發(fā)難題。當(dāng)然,每次面試都是不同的,你也可能不會(huì)遇見這類問題。但是知道的越多,準(zhǔn)備的就越充分。


          第一部分:突發(fā)難題


          如果在面試中突然問到下列問題,似乎很難回答。即便如此,這些問題在準(zhǔn)備中仍發(fā)揮作用:它們揭示了JavaScript的一些有趣的功能,并強(qiáng)調(diào)在提出編程語(yǔ)言時(shí),首先必須做出的一些決定。


          了解有關(guān)JavaScript的更多功能,建議訪問https://wtfjs.com。


          1. 為什么Math.max()小于Math.min()?


          Math.max()> Math.min()輸出錯(cuò)誤這一說法看上去有問題,但其實(shí)相當(dāng)合理。


          如果沒有給出參數(shù),Math.min()返回infinity(無(wú)窮大),Math.max()返回-infinity(無(wú)窮小)。這只是max()和min()方法規(guī)范的一部分,但選擇背后的邏輯值得深議。了解其中原因,請(qǐng)看以下代碼:

          Math.min(1) // 1
          Math.min(1, infinity)// 1
          Math.min(1, -infinity)// -infinity
          


          如果-infinity(無(wú)窮小)作為Math.min()的默認(rèn)參數(shù),那么每個(gè)結(jié)果都是-infinity(無(wú)窮小),這毫無(wú)用處! 然而,如果默認(rèn)參數(shù)是infinity(無(wú)窮大),則無(wú)論添加任何參數(shù)返回都會(huì)是該數(shù)字 - 這就是我們想要的運(yùn)行方式。


          2. 為什么0.1+0.2不等于0.3


          簡(jiǎn)而言之,這與JavaScript在二進(jìn)制中存儲(chǔ)浮點(diǎn)數(shù)的準(zhǔn)確程度有關(guān)。在Google Chrome控制臺(tái)中輸入以下公式將得到:

          0.1 + 0.2// 0.30000000000000004
           
          0.1 + 0.2 - 0.2// 0.10000000000000003
           
          0.1 + 0.7// 0.7999999999999999
          


          如果是簡(jiǎn)單的等式,對(duì)準(zhǔn)確度沒有要求,這不太可能產(chǎn)生問題。但是如果需要測(cè)試相等性,即使是簡(jiǎn)單地應(yīng)用也會(huì)導(dǎo)致令人頭疼的問題。解決這些問題,有以下幾種方案。


          Fixed Point固定點(diǎn)


          例如,如果知道所需的最大精度(例如,如果正在處理貨幣),則可以使用整數(shù)類型來(lái)存儲(chǔ)該值。因此,可以存儲(chǔ)499而非4.99美元,并在此基礎(chǔ)上執(zhí)行任何等式,然后可以使用類似result =(value / 100).toFixed(2)的表達(dá)式將結(jié)果顯示給最終用戶,該表達(dá)式返回一個(gè)字符串。


          BCD代碼


          如果精度非常重要,另一種方法是使用二進(jìn)制編碼的十進(jìn)制(BCD)格式,可以使用BCD庫(kù)(https://formats.kaitai.io/bcd/javascript.html)訪問JavaScript。每個(gè)十進(jìn)制值分別存儲(chǔ)在一個(gè)字節(jié)(8位)中。鑒于一個(gè)字節(jié)可以存儲(chǔ)16個(gè)單獨(dú)值,而該系統(tǒng)僅使用0-9位,所以這種方法效率低下。但是,如果十分注重精確度,采用何種方法都值得考量。


          3. 為什么018減017等于3?


          018-017返回3實(shí)際是靜默類型轉(zhuǎn)換的結(jié)果。這種情況,討論的是八進(jìn)制數(shù)。


          八進(jìn)制數(shù)簡(jiǎn)介


          你或許知道計(jì)算中使用二進(jìn)制(base-2)和十六進(jìn)制(base-16)數(shù)字系統(tǒng),但是八進(jìn)制(base-8)在計(jì)算機(jī)歷史中的地位也舉足親重:在20世紀(jì)50年代后期和 20世紀(jì)60年代間,八進(jìn)制被用于簡(jiǎn)化二進(jìn)制,削減高昂的制造系統(tǒng)中的材料成本。


          不久以后Hexadecimal(十六進(jìn)制)開始登上歷史舞臺(tái):

          1965年發(fā)布的IBM360邁出了從八進(jìn)制到十六進(jìn)制的決定性一步。我們這些習(xí)慣八進(jìn)制的人對(duì)這一舉措感到震驚!沃恩·普拉特(Vaughan Pratt)


          如今的八進(jìn)制數(shù)


          但在現(xiàn)代編程語(yǔ)言中,八進(jìn)制又有何作用呢?針對(duì)某些案例,八進(jìn)制比十六進(jìn)制更具優(yōu)勢(shì),因?yàn)樗恍枰魏畏菙?shù)字(使用0-7而不是0-F)。

          一個(gè)常見用途是Unix系統(tǒng)的文件權(quán)限,其中有八個(gè)權(quán)限變體:

          4 2 1

          0 - - - no permissions

          1 - - x only execute

          2 - x - only write

          3 - x x write and execute

          4 x - - only read

          5 x - x read and execute

          6 x x - read and write

          7 x x x read, write and execute

          出于相似的原由,八進(jìn)制也用于數(shù)字顯示器。


          回到問題本身


          在JavaScript中,前綴0將所有數(shù)字轉(zhuǎn)換為八進(jìn)制。但是,八進(jìn)制中不使用數(shù)字8,任何包含8的數(shù)字都將自動(dòng)轉(zhuǎn)換為常規(guī)十進(jìn)制數(shù)。

          因此,018-017實(shí)際上等同于十進(jìn)制表達(dá)式:18-15,因?yàn)?17使用八進(jìn)制而018使用十進(jìn)制。

          第二部分:常見問題


          圖片來(lái)源:pexels.com/@divinetechygirl


          本節(jié)中,將介紹面試中一些更加常見的JavaScript問題。第一次學(xué)習(xí)JavaScript時(shí),這些問題容易被忽略。但在編寫最佳代碼時(shí),了解下述問題用處頗大。


          4. 函數(shù)表達(dá)式與函數(shù)聲明有哪些不同?


          函數(shù)聲明使用關(guān)鍵字function,后跟函數(shù)的名稱。相反,函數(shù)表達(dá)式以var,let或const開頭,后跟函數(shù)名稱和賦值運(yùn)算符=。請(qǐng)看以下代碼:


          // Function Declaration
          function sum(x, y) {
           return x + y;
          };
          // Function Expression: ES5
          var sum = function(x, y) {
           return x + y;
          };
           
          // Function Expression: ES6+
          const sum = (x, y) => { return x + y };
          


          實(shí)際操作中,關(guān)鍵的區(qū)別在于函數(shù)聲明要被提升,而函數(shù)表達(dá)式則沒有。這意味著JavaScript解釋器將函數(shù)聲明移動(dòng)到其作用域的頂部,因此可以定義函數(shù)聲明并在代碼中的任何位置調(diào)用它。相比之下,只能以線性順序調(diào)用函數(shù)表達(dá)式:必須在調(diào)用它之前解釋。

          如今,許多開發(fā)人員偏愛函數(shù)表達(dá)式有如下幾個(gè)原因:

          · 首先,函數(shù)表達(dá)式實(shí)施更加可預(yù)測(cè)的結(jié)構(gòu)化代碼庫(kù)。當(dāng)然,函數(shù)聲明也可使用結(jié)構(gòu)化代碼庫(kù); 只是函數(shù)聲明讓你更容易擺脫凌亂的代碼。

          · 其次,可以將ES6語(yǔ)法用于函數(shù)表達(dá)式:這通常更為簡(jiǎn)潔,let和const可以更好地控制是否重新賦值變量,我們將在下一個(gè)問題中看到。


          5. var,let和const有什么區(qū)別?


          自ES6發(fā)布以來(lái),現(xiàn)代語(yǔ)法已進(jìn)入各行各業(yè),這已是一個(gè)極其常見的面試問題。Var是第一版JavaScript中的變量聲明關(guān)鍵字。但它的缺點(diǎn)導(dǎo)致在ES6中采用了兩個(gè)新關(guān)鍵字:let和const。


          這三個(gè)關(guān)鍵字具有不同的分配,提升和域 - 因此我們將單獨(dú)討論。


          i) 分配


          最基本的區(qū)別是let和var可以重新分配,而const則不能。這使得const成為不變變量的最佳選擇,并且它將防止諸如意外重新分配之類的失誤。注意,當(dāng)變量表示數(shù)組或?qū)ο髸r(shí),const確實(shí)允許變量改變,只是無(wú)法重新分配變量本身。


          Let 和var都可重新分配,但是正如以下幾點(diǎn)應(yīng)該明確的那樣,如果不是所有情況都要求更改變量,多數(shù)選擇中,let具有優(yōu)于var的顯著優(yōu)勢(shì)。


          ii)提升


          與函數(shù)聲明和表達(dá)式(如上所述)之間的差異類似,使用var聲明的變量總是被提升到它們各自的頂部,而使用const和let聲明的變量被提升,但是如果你試圖在聲明之前訪問,將會(huì)得到一個(gè)TDZ(時(shí)間死區(qū))錯(cuò)誤。由于var可能更容易出錯(cuò),例如意外重新分配,因此運(yùn)算是有用的。請(qǐng)看以下代碼:

          var x = "global scope";
          function foo() {
           var x = "functional scope";
           console.log(x);
          }
          foo(); // "functional scope"
          console.log(x); // "global scope"
          


          這里,foo()和console.log(x)的結(jié)果與預(yù)期一致。但是,如果去掉第二個(gè)變量又會(huì)發(fā)生什么呢?

          var x = "global scope";
          function foo() {
           x = "functional scope";
           console.log(x);
          }
          foo(); // "functional scope"
          console.log(x); // "functional scope"
          


          盡管在函數(shù)內(nèi)定義,但x =“functional scope”已覆蓋全局變量。需要重復(fù)關(guān)鍵字var來(lái)指定第二個(gè)變量x僅限于foo()。


          iii) 域


          雖然var是function-scoped(函數(shù)作用域),但let和const是block-scoped(塊作用域的:一般情況下,Block是大括號(hào){}內(nèi)的任何代碼,包括函數(shù),條件語(yǔ)句和循環(huán)。為了闡明差異,請(qǐng)看以下代碼:

          var a = 0;
          let b = 0;
          const c = 0;
          if (true) {
           var a = 1;
           let b = 1;
           const c = 1;
          }
          console.log(a); // 1
          console.log(b); // 0
          console.log(c); // 0
          


          在條件塊中,全局范圍的var a已重新定義,但全局范圍的let b和const c則沒有。一般而言,確保本地任務(wù)保持在本地執(zhí)行,將使代碼更加清晰,減少出錯(cuò)。


          6. 如果分配不帶關(guān)鍵字的變量會(huì)發(fā)生什么?


          如果不使用關(guān)鍵字定義變量,又會(huì)如何?從技術(shù)上講,如果x尚未定義,則x = 1是window.x = 1的簡(jiǎn)寫。


          要想完全杜絕這種簡(jiǎn)寫,可以編寫嚴(yán)格模式,——在ES5中介紹過——在文檔頂部或特定函數(shù)中寫use strict。后,當(dāng)你嘗試聲明沒有關(guān)鍵字的變量時(shí),你將收到一條報(bào)語(yǔ)法錯(cuò)誤:Uncaught SyntaxError:Unexpected indentifier。


          7. 面向?qū)ο缶幊蹋∣OP)和函數(shù)式編程(FP)之間的區(qū)別是什么?


          JavaScript是一種多范式語(yǔ)言,即它支持多種不同的編程風(fēng)格,包括事件驅(qū)動(dòng),函數(shù)和面向?qū)ο蟆?/p>


          編程范式各有不同,但在當(dāng)代計(jì)算中,函數(shù)編程和面向?qū)ο缶幊套顬榱餍?- 而JavaScript兩種都可執(zhí)行。


          面向?qū)ο缶幊?/strong>


          OOP以“對(duì)象”這一概念為基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu),包含數(shù)據(jù)字段(JavaScript稱為類)和程序(JavaScript中的方法)。


          一些JavaScript的內(nèi)置對(duì)象包括Math(用于random,max和sin等方法),JSON(用于解析JSON數(shù)據(jù))和原始數(shù)據(jù)類型,如String,Array,Number和Boolean。


          無(wú)論何時(shí)采用的內(nèi)置方法,原型或類,本質(zhì)上都在使用面向?qū)ο缶幊獭?/p>


          函數(shù)編程


          FP(函數(shù)編程)以“純函數(shù)”的概念為基礎(chǔ),避免共享狀態(tài),可變數(shù)據(jù)和副作用。這可能看起來(lái)像很多術(shù)語(yǔ),但可能已經(jīng)在代碼中創(chuàng)建了許多純函數(shù)。


          輸入相同數(shù)據(jù),純函數(shù)總是返回相同的輸出。這種方式?jīng)]有副作用:除了返回結(jié)果之外,例如登錄控制臺(tái)或修改外部變量等都不會(huì)發(fā)生。


          至于共享狀態(tài),這里有一個(gè)簡(jiǎn)單的例子,即使輸入是相同的,狀態(tài)仍可以改變函數(shù)的輸出。設(shè)置一個(gè)具有兩個(gè)函數(shù)的代碼:一個(gè)將數(shù)字加5,另一個(gè)將數(shù)字乘以5。

          const num = {
           val: 1
          };
          const add5 = () => num.val += 5;
          const multiply5 = () => num.val *= 5;
          


          如果先調(diào)用add5在調(diào)用乘以5,則整體結(jié)果為30。但是如果以相反的方式執(zhí)行函數(shù)并記錄結(jié)果,則輸出為10,與之前結(jié)果不一致。


          這違背了函數(shù)式編程的原理,因?yàn)楹瘮?shù)的結(jié)果因Context調(diào)用方法而異。 重新編寫上面的代碼,以便結(jié)果更易預(yù)測(cè):

          const num = {
           val: 1
          };
          const add5 = () => Object.assign({}, num, {val: num.val + 5}); const multiply5 = () => Object.assign({}, num, {val: num.val * 5});
          


          現(xiàn)在,num.val的值仍然為1,無(wú)論Context調(diào)用的方法如何,add5(num)和multiply5(num)將始終輸出相同的結(jié)果。


          8. 命令式和聲明性編程之間有什么區(qū)別?


          關(guān)于命令式編程和聲明式編程的區(qū)別,可以以O(shè)OP(面向?qū)ο缶幊蹋┖虵P(函數(shù)式編程)為參考。


          這兩種是描述多種不同編程范式共有特征的概括性術(shù)語(yǔ)。FP(函數(shù)式編程)是聲明性編程的一個(gè)范例,而OOP(面向?qū)ο缶幊蹋┦敲钍骄幊痰囊粋€(gè)范例。


          從基本的意義層面,命令式編程關(guān)注的是如何做某事。它以最基本的方式闡明了步驟,并以for和while循環(huán),if和switch陳述句等為特征。

          const sumArray = array => {
           let result = 0;
           for (let i = 0; i < array.length; i++) {
           result += array[i]
           };
           return result;
          }
          


          相比之下,聲明性編程關(guān)注的是做什么,它通過依賴表達(dá)式將怎樣做抽出來(lái)。這通常會(huì)產(chǎn)生更簡(jiǎn)潔的代碼,但是在規(guī)模上,由于透明度低,調(diào)試會(huì)更加困難。


          這是上述的sumArray()函數(shù)的聲明方法。

          const sumArray = array => { return array.reduce((x, y) => x + y) };
          


          圖片來(lái)源:pexels.com/@rawpixel


          9. 是什么基于原型的繼承?


          最后,要講到的是基于原型的繼承。面向?qū)ο缶幊逃袔追N不同的類型,JavaScript使用的是基于原型的繼承。該系統(tǒng)通過使用現(xiàn)有對(duì)象作為原型,允許重復(fù)運(yùn)行。


          即使是首次遇到原型這一概念,使用內(nèi)置方法時(shí)也會(huì)遇到原型系統(tǒng)。 例如,用于操作數(shù)組的函數(shù)(如map,reduce,splice等)都是Array.prototype對(duì)象的方法。實(shí)際上,數(shù)組的每個(gè)實(shí)例(使用方括號(hào)[]定義,或者 -不常見的 new Array())都繼承自Array.prototype,這就是為什么map,reduce和spliceare等方法都默認(rèn)可用的原因。


          幾乎所有內(nèi)置對(duì)象都是如此,例如字符串和布爾運(yùn)算:只有少數(shù),如Infinity,NaN,null和undefined等沒有類或方法。


          在原型鏈的末尾,能發(fā)現(xiàn) Object.prototype,幾乎JavaScript中的每個(gè)對(duì)象都是Object的一個(gè)實(shí)例。比如Array. prototype和String. prototype都繼承了Object.prototype的類和方法。


          要想對(duì)使用prototype syntax的對(duì)象添加類和方法,只需將對(duì)象作為函數(shù)啟動(dòng),并使用prototype關(guān)鍵字添加類和方法:

          function Person() {};
          Person.prototype.forename = "John";
          Person.prototype.surname = "Smith";
          


          是否應(yīng)該覆蓋或擴(kuò)展原型運(yùn)算?


          可以使用與創(chuàng)建擴(kuò)展prototypes同樣的方式改變內(nèi)置運(yùn)算,但是大多數(shù)開發(fā)人員(以及大多數(shù)公司)不會(huì)建議這樣做。


          如果希望多個(gè)對(duì)象進(jìn)行同樣的運(yùn)算,可以創(chuàng)建一個(gè)自定義對(duì)象(或定義你自己的“類”或“子類”),這些對(duì)象繼承內(nèi)置原型而不改變?cè)捅旧怼H绻蛩闩c其他開發(fā)人員合作,他們對(duì)JavaScript的默認(rèn)行為有一定的預(yù)期,編輯此默認(rèn)行為很容易導(dǎo)致出錯(cuò)。


          總的來(lái)說,這些問題能夠幫助你更好理解JavaScript,包括其核心功能和其他鮮為人知的功能 ,并且望能助你為下次的面試做好準(zhǔn)備。

          留言 點(diǎn)贊 關(guān)注

          我們一起分享AI學(xué)習(xí)與發(fā)展的干貨

          歡迎關(guān)注全平臺(tái)AI垂類自媒體 “讀芯術(shù)”

          迎使用.NET 6。今天的版本是.NET 團(tuán)隊(duì)和社區(qū)一年多努力的結(jié)果。C# 10 和F# 6 提供了語(yǔ)言改進(jìn),使您的代碼更簡(jiǎn)單、更好。性能大幅提升,我們已經(jīng)看到微軟降低了托管云服務(wù)的成本。.NET 6 是第一個(gè)原生支持Apple Silicon (Arm64) 的版本,并且還針對(duì)Windows Arm64 進(jìn)行了改進(jìn)。我們構(gòu)建了一個(gè)新的動(dòng)態(tài)配置文件引導(dǎo)優(yōu)化(PGO) 系統(tǒng),該系統(tǒng)可提供僅在運(yùn)行時(shí)才可能進(jìn)行的深度優(yōu)化。使用dotnet monitor和OpenTelemetry改進(jìn)了云診斷。WebAssembly支持更有能力和性能。為HTTP/3添加了新的API ,處理JSON、數(shù)學(xué)和直接操作內(nèi)存。.NET 6 將支持三年。開發(fā)人員已經(jīng)開始將應(yīng)用程序升級(jí)到.NET 6,我們?cè)谏a(chǎn)中聽到了很好的早期成果。.NET 6 已為您的應(yīng)用程序做好準(zhǔn)備。

          您可以下載適用于Linux、macOS 和Windows 的.NET 6 。

          • 安裝程序和二進(jìn)制文件
          • 容器圖像
          • Linux 軟件包
          • 發(fā)行說明
          • API 差異
          • 已知的問題
          • GitHub 問題跟蹤器

          請(qǐng)參閱ASP.NET Core、Entity Framework、Windows Forms、.NET MAUI、YARP和dotnet 監(jiān)視器帖子,了解各種場(chǎng)景中的新增功能。

          .NET 6 亮點(diǎn)

          .NET 6 是:

          • 使用Microsoft 服務(wù)、其他公司運(yùn)行的云應(yīng)用程序和開源項(xiàng)目進(jìn)行生產(chǎn)壓力測(cè)試
          • 作為最新的長(zhǎng)期支持(LTS) 版本支持三年
          • 跨瀏覽器、云、桌面、物聯(lián)網(wǎng)和移動(dòng)應(yīng)用程序的統(tǒng)一平臺(tái) ,所有這些都使用相同的.NET 庫(kù)并能夠輕松共享代碼。
          • 性能 得到了極大的提高,尤其是文件I/O,這共同導(dǎo)致了執(zhí)行時(shí)間、延遲和內(nèi)存使用的減少。
          • C# 10提供了語(yǔ)言改進(jìn),例如記錄結(jié)構(gòu)、隱式使用和新的lambda 功能,而編譯器添加了增量源生成器。 F# 6 添加了新功能,包括基于任務(wù)的異步、管道調(diào)試和眾多性能改進(jìn)。
          • Visual Basic 在Visual Studio 體驗(yàn)和Windows 窗體項(xiàng)目打開體驗(yàn)方面有所改進(jìn)。
          • Hot Reload 使您能夠跳過重新構(gòu)建和重新啟動(dòng)您的應(yīng)用程序以查看新更改(在您的應(yīng)用程序運(yùn)行時(shí)),Visual Studio 2022 和.NET CLI 支持C# 和Visual Basic。
          • 云診斷 已通過OpenTelemetry和dotnet monitor得到改進(jìn),現(xiàn)在在生產(chǎn)中支持并在Azure 應(yīng)用服務(wù)中可用。
          • JSON API 的功能更強(qiáng)大,并且通過序列化程序的源生成器具有更高的性能。
          • ASP.NET Core中引入的 最小 API可簡(jiǎn)化入門體驗(yàn)并提高HTTP 服務(wù)的性能。
          • Blazor組件現(xiàn)在可以從JavaScript 呈現(xiàn)并與現(xiàn)有的基于JavaScript 的應(yīng)用程序集成。
          • Blazor WebAssembly (Wasm) 應(yīng)用程序的WebAssembly AOT 編譯,以及對(duì)運(yùn)行時(shí)重新鏈接和本機(jī)依賴項(xiàng)的支持。
          • 使用 http://ASP.NET Core 構(gòu)建的單頁(yè)應(yīng)用程序現(xiàn)在使用更靈活的模式,可與Angular、React 和其他流行的前端 JavaScript 框架一起使用。
          • 添加了 HTTP/3,以便http://ASP.NET Core、HttpClient 和gRPC 都可以與HTTP/3 客戶端和服務(wù)器交互。
          • File IO 現(xiàn)在支持符號(hào)鏈接,并且通過re-written-from-scratch 大大提高了性能FileStream。
          • 通過支持OpenSSL 3、ChaCha20Poly1305 加密方案和運(yùn)行時(shí)深度防御緩解,特別是W^X和CET ,安全 性得到了改進(jìn)。
          • 可以為L(zhǎng)inux、macOS 和Windows(以前只有Linux)發(fā)布 單文件應(yīng)用程序(免提取)。
          • IL 修整 現(xiàn)在更加強(qiáng)大和有效,新的警告和分析器可確保正確的最終結(jié)果。
          • 添加了源生成器和分析器 ,可幫助您生成更好、更安全和更高性能的代碼。
          • 源代碼構(gòu)建 使Red Hat 等組織能夠從源代碼構(gòu)建.NET 并向其用戶提供自己的構(gòu)建。

          該版本包括大約一萬(wàn)次git 提交。即使這篇文章很長(zhǎng),它也跳過了許多改進(jìn)。您必須下載并試用.NET 6 才能看到所有新功能。

          支持

          .NET 6 是一個(gè)長(zhǎng)期支持(LTS) 版本,將支持三年。它支持多種操作系統(tǒng),包括macOS Apple Silicon 和Windows Arm64。

          Red Hat與.NET 團(tuán)隊(duì)合作,在Red Hat Enterprise Linux 上支持.NET。在RHEL 8 及更高版本上,.NET 6 將可用于AMD 和Intel (x64_64)、ARM (aarch64) 以及IBM Z 和LinuxONE (s390x) 架構(gòu)。

          請(qǐng)開始將您的應(yīng)用程序遷移到.NET 6,尤其是.NET 5 應(yīng)用程序。我們從早期采用者那里聽說,從.NET Core 3.1 和.NET 5 升級(jí)到.NET 6 很簡(jiǎn)單。

          Visual Studio 2022和Visual Studio 2022 for Mac支持.NET 6 。Visual Studio 2019、Visual Studio for Mac 8 或MSBuild 16 不支持它。如果要使用.NET 6,則需要升級(jí)到Visual Studio 2022(現(xiàn)在也是64 位)。Visual Studio Code C# 擴(kuò)展支持.NET 6 。

          Azure App 服務(wù):

          • Azure Functions現(xiàn)在支持在.NET 6 中運(yùn)行無(wú)服務(wù)器函數(shù)。
          • App Service .NET 6 GA Announcement為ASP.NET Core 開發(fā)人員提供了信息和詳細(xì)信息,他們很高興今天開始使用.NET 6。
          • Azure 靜態(tài)Web 應(yīng)用現(xiàn)在支持帶有Blazor WebAssembly 前端和Azure Function API 的全棧.NET 6 應(yīng)用程序。

          注意:如果您的應(yīng)用已經(jīng)在應(yīng)用服務(wù)上運(yùn)行.NET 6 預(yù)覽版或RC 版本,則在將.NET 6 運(yùn)行時(shí)和SDK 部署到您所在區(qū)域后,它將在第一次重新啟動(dòng)時(shí)自動(dòng)更新。如果您部署了一個(gè)獨(dú)立的應(yīng)用程序,您將需要重新構(gòu)建和重新部署。

          統(tǒng)一擴(kuò)展平臺(tái)

          .NET 6 為瀏覽器、云、桌面、物聯(lián)網(wǎng)和移動(dòng)應(yīng)用程序提供了一個(gè)統(tǒng)一的平臺(tái)。底層平臺(tái)已更新,可滿足所有應(yīng)用類型的需求,并便于在所有應(yīng)用中重用代碼。新功能和改進(jìn)同時(shí)適用于所有應(yīng)用程序,因此您在云或移動(dòng)設(shè)備上運(yùn)行的代碼的行為方式相同并具有相同的優(yōu)勢(shì)。

          .NET 開發(fā)人員的范圍隨著每個(gè)版本的發(fā)布而不斷擴(kuò)大。機(jī)器學(xué)習(xí)和WebAssembly是最近添加的兩個(gè)。例如,通過機(jī)器學(xué)習(xí),您可以編寫在流數(shù)據(jù)中查找異常的應(yīng)用程序。使用WebAssembly,您可以在瀏覽器中托管.NET 應(yīng)用程序,就像HTML 和JavaScript 一樣,或者將它們與HTML 和JavaScript 混合使用。

          最令人興奮的新增功能之一是.NET Multi-platform App UI (.NET MAUI)。您現(xiàn)在可以在單個(gè)項(xiàng)目中編寫代碼,從而跨桌面和移動(dòng)操作系統(tǒng)提供現(xiàn)代客戶端應(yīng)用程序體驗(yàn)。.NET MAUI 將比.NET 6 稍晚發(fā)布。我們?cè)?NET MAUI 上投入了大量時(shí)間和精力,很高興能夠發(fā)布它并看到.NET MAUI 應(yīng)用程序投入生產(chǎn)。

          當(dāng)然,.NET 應(yīng)用程序也可以在家中使用Windows 桌面(使用Windows Forms和WPF)以及使用ASP.NET Core 在云中。它們是我們提供時(shí)間最長(zhǎng)的應(yīng)用程序類型,并且仍然非常受歡迎,我們?cè)?NET 6 中對(duì)其進(jìn)行了改進(jìn)。

          面向 .NET 6

          繼續(xù)以廣泛平臺(tái)為主題,在所有這些操作系統(tǒng)上編寫.NET 代碼很容易。

          要以 .NET 6 為目標(biāo),您需要使用.NET 6 目標(biāo)框架,如下所示:

          <TargetFramework>net6.0<TargetFramework>

          net6.0 Target Framework Moniker (TFM) 使您可以訪問.NET 提供的所有跨平臺(tái)API。如果您正在編寫控制臺(tái)應(yīng)用程序、http://ASP.NET Core 應(yīng)用程序或可重用的跨平臺(tái)庫(kù),這是最佳選擇。

          如果您針對(duì)特定操作系統(tǒng)(例如編寫Windows 窗體或iOS 應(yīng)用程序),那么還有另一組TFM(每個(gè)都針對(duì)不言而喻的操作系統(tǒng))供您使用。它們使您可以訪問所有net6.0的API以及一堆特定于操作系統(tǒng)的API。

          • net6.0-android
          • net6.0-ios
          • net6.0-maccatalyst
          • net6.0-tvos
          • net6.0-windows

          每個(gè)無(wú)版本TFM 都相當(dāng)于針對(duì).NET 6 支持的最低操作系統(tǒng)版本。如果您想要具體或訪問更新的API,可以指定操作系統(tǒng)版本。

          net6.0和net6.0-windows TFMs 都支持(與.NET 5 相同)。Android 和Apple TFM 是.NET 6 的新功能,目前處于預(yù)覽階段。稍后的.NET 6 更新將支持它們。

          操作系統(tǒng)特定的 TFM 之間沒有兼容性關(guān)系。 例如,net6.0-ios與 net6.0-tvos不兼容。 如果您想共享代碼,您需要使用帶有#if 語(yǔ)句的源代碼或帶有net6.0目標(biāo)代碼的二進(jìn)制文件來(lái)實(shí)現(xiàn)。

          性能

          自從我們啟動(dòng).NET Core 項(xiàng)目以來(lái),該團(tuán)隊(duì)一直在不斷地關(guān)注性能。Stephen Toub在記錄每個(gè)版本的.NET 性能進(jìn)展方面做得非常出色。歡迎查看在.NET 6 中的性能改進(jìn)的帖子。在這篇文章中,里面包括您想了解的重大性能改進(jìn),包括文件IO、接口轉(zhuǎn)換、PGO 和System.Text.Json。

          動(dòng)態(tài) PGO

          動(dòng)態(tài)輪廓引導(dǎo)優(yōu)化(PGO)可以顯著提高穩(wěn)態(tài)性能。例如,PGO 為TechEmpower JSON"MVC"套件的每秒請(qǐng)求數(shù)提高了26%(510K -> 640K)。

          動(dòng)態(tài)PGO 建立在分層編譯的基礎(chǔ)上,它使方法能夠首先非常快速地編譯(稱為"第0 層")以提高啟動(dòng)性能,然后在啟用大量?jī)?yōu)化的情況下隨后重新編譯(稱為"第1 層")一旦該方法被證明是有影響的。該模型使方法能夠在第0 層中進(jìn)行檢測(cè),以允許對(duì)代碼的執(zhí)行進(jìn)行各種觀察。在第1 層重新調(diào)整這些方法時(shí),從第0 層執(zhí)行收集的信息用于更好地優(yōu)化第1 層代碼。這就是機(jī)制的本質(zhì)。

          動(dòng)態(tài)PGO 的啟動(dòng)時(shí)間將比默認(rèn)運(yùn)行時(shí)稍慢,因?yàn)樵诘? 層方法中運(yùn)行了額外的代碼來(lái)觀察方法行為。

          要啟用動(dòng)態(tài) PGO,請(qǐng)?jiān)趹?yīng)用程序?qū)⑦\(yùn)行的環(huán)境中設(shè)置 DOTNET_TieredPGO=1。 您還必須確保啟用分層編譯(默認(rèn)情況下)。 動(dòng)態(tài) PGO 是可選的,因?yàn)樗且环N新的且有影響力的技術(shù)。 我們希望發(fā)布選擇加入使用和相關(guān)反饋,以確保它經(jīng)過全面壓力測(cè)試。 我們對(duì)分層編譯做了同樣的事情。 至少一個(gè)非常大的 Microsoft 服務(wù)支持并已在生產(chǎn)中使用動(dòng)態(tài) PGO。 我們鼓勵(lì)您嘗試一下。

          您可以在.NET 6中的性能帖子中看到更多關(guān)于動(dòng)態(tài)PGO 優(yōu)勢(shì)的信息,包括以下微基準(zhǔn),它測(cè)量特定LINQ 枚舉器的成本。

          private IEnumerator<long> _source = Enumerable.Range(0, long.MaxValue).GetEnumerator();
          
          [Benchmark]
          public void MoveNext() => _source.MoveNext();

          這是有和沒有動(dòng)態(tài)PGO 的結(jié)果。

          這是一個(gè)相當(dāng)大的差異,但代碼大小也有所增加,這可能會(huì)讓一些讀者感到驚訝。這是由JIT 生成的匯編代碼的大小,而不是內(nèi)存分配(這是一個(gè)更常見的焦點(diǎn))。.NET 6 性能帖子對(duì)此有很好的解釋。

          PGO 實(shí)現(xiàn)中常見的一種優(yōu)化是"熱/冷分離",其中經(jīng)常執(zhí)行的方法部分(“熱”)在方法開始時(shí)靠近在一起,而不經(jīng)常執(zhí)行的方法部分(“冷”)是移到方法的末尾。這樣可以更好地使用指令緩存,并最大限度地減少可能未使用的代碼負(fù)載。

          作為上下文,接口調(diào)度是 .NET 中最昂貴的調(diào)用類型。 非虛擬方法調(diào)用是最快的,甚至更快的是可以通過內(nèi)聯(lián)消除的調(diào)用。 在這種情況下,動(dòng)態(tài) PGO 為 MoveNext 提供了兩個(gè)(替代)調(diào)用站點(diǎn)。 第一個(gè) - 熱的 - 是對(duì) Enumerable+RangeIterator.MoveNext的直接調(diào)用,另一個(gè) - 冷的 - 是通過 IEnumerator<int>的虛擬接口調(diào)用。 如果大多數(shù)時(shí)候最熱門的人都被叫到,那將是一個(gè)巨大的勝利。

          這就是魔法。當(dāng) JIT 檢測(cè)此方法的第 0 層代碼時(shí),包括檢測(cè)此接口調(diào)度以跟蹤每次調(diào)用時(shí) \_source的具體類型。 JIT 發(fā)現(xiàn)每次調(diào)用都在一個(gè)名為 Enumerable+RangeIterator的類型上,這是一個(gè)私有類,用于在 Enumerable實(shí)現(xiàn)內(nèi)部實(shí)現(xiàn) Enumerable.Range。因此,對(duì)于第 1 層,JIT 已發(fā)出檢查以查看 \_source的類型是否為 Enumerable+RangeIterator:如果不是,則跳轉(zhuǎn)到我們之前強(qiáng)調(diào)的執(zhí)行正常接口調(diào)度的冷部分。但如果是 - 基于分析數(shù)據(jù),預(yù)計(jì)絕大多數(shù)時(shí)間都是這種情況 - 然后它可以繼續(xù)直接調(diào)用非虛擬化的 Enumerable+RangeIterator.MoveNext方法。不僅如此,它還認(rèn)為內(nèi)聯(lián) MoveNext 方法是有利可圖的。最終效果是生成的匯編代碼有點(diǎn)大,但針對(duì)預(yù)期最常見的確切場(chǎng)景進(jìn)行了優(yōu)化。當(dāng)我們開始構(gòu)建動(dòng)態(tài) PGO 時(shí),這些就是我們想要的那種勝利。

          動(dòng)態(tài)PGO 將在RyuJIT 部分再次討論。

          文件 IO 改進(jìn)

          FileStream幾乎完全用.NET 6 重寫,重點(diǎn)是提高異步文件IO 性能。在Windows 上,實(shí)現(xiàn)不再使用阻塞API,并且可以 快幾倍 !我們還改進(jìn)了所有平臺(tái)上的內(nèi)存使用。在第一次異步操作(通常分配)之后,我們已經(jīng)使異步操作 免分配 !此外,我們已經(jīng)使Windows 和Unix 實(shí)現(xiàn)不同的邊緣情況的行為統(tǒng)一(這是可能的)。

          這種重寫的性能改進(jìn)使所有操作系統(tǒng)受益。對(duì)Windows 的好處是最大的,因?yàn)樗h(yuǎn)遠(yuǎn)落后。macOS 和Linux 用戶也應(yīng)該會(huì)看到顯著FileStream的性能改進(jìn)。

          以下基準(zhǔn)將100 MB 寫入新文件。

          private byte[] _bytes = new byte[8_000];
          
          [Benchmark]
          public async Task Write100MBAsync()
          {
              using FileStream fs = new("file.txt", FileMode.Create, FileAccess.Write, FileShare.None, 1, FileOptions.Asynchronous);
              for (int i = 0; i < 100_000_000 / 8_000; i++)
                  await fs.WriteAsync(_bytes);
          }

          在帶有SSD 驅(qū)動(dòng)器的Windows 上,我們觀察到 4倍的加速 和超過 1200倍的分配下降

          我們還認(rèn)識(shí)到需要更高性能的文件 IO 功能:并發(fā)讀取和寫入,以及分散/收集 IO。 針對(duì)這些情況,我們?yōu)?System.IO.FileSystem.IO.RandomAccess類引入了新的 API。

          async Task AllOrNothingAsync(string path, IReadOnlyList<ReadOnlyMemory<byte>> buffers)
          {
              using SafeFileHandle handle = File.OpenHandle(
                  path, FileMode.Create, FileAccess.Write, FileShare.None, FileOptions.Asynchronous,
                  preallocationSize: buffers.Sum(buffer => buffer.Length)); // hint for the OS to pre-allocate disk space
          
              await RandomAccess.WriteAsync(handle, buffers, fileOffset: 0); // on Linux it's translated to a single sys-call!
          }

          該示例演示:

          • 使用新File.OpenHandle API打開文件句柄。
          • 使用新的預(yù)分配大小功能預(yù)分配磁盤空間。
          • 使用新的Scatter/Gather IO API寫入文件。

          預(yù)分配大小功能提高了性能,因?yàn)閷懭氩僮鞑恍枰獢U(kuò)展文件,并且文件不太可能被碎片化。這種方法提高了可靠性,因?yàn)閷懭氩僮鲗⒉辉僖蚩臻g不足而失敗,因?yàn)榭臻g已被保留。Scatter/Gather IO API 減少了寫入數(shù)據(jù)所需的系統(tǒng)調(diào)用次數(shù)。

          更快的接口檢查和轉(zhuǎn)換

          界面鑄造性能提高了16% - 38%。這種改進(jìn)對(duì)于C# 與接口之間的模式匹配特別有用。

          這張圖表展示了一個(gè)有代表性的基準(zhǔn)測(cè)試的改進(jìn)規(guī)模。

          將.NET 運(yùn)行時(shí)的一部分從C++ 遷移到托管C# 的最大優(yōu)勢(shì)之一是它降低了貢獻(xiàn)的障礙。這包括接口轉(zhuǎn)換,它作為早期的.NET 6 更改移至C#。.NET 生態(tài)系統(tǒng)中懂C# 的人比懂C++ 的人多(而且運(yùn)行時(shí)使用具有挑戰(zhàn)性的C++ 模式)。僅僅能夠閱讀構(gòu)成運(yùn)行時(shí)的一些代碼是培養(yǎng)以各種形式做出貢獻(xiàn)的信心的重要一步。

          歸功于 Ben Adams。

          System.Text.Json 源生成器

          我們?yōu)镾ystem.Text.Json 添加了一個(gè)源代碼生成器,它避免了在運(yùn)行時(shí)進(jìn)行反射和代碼生成的需要,并且可以在構(gòu)建時(shí)生成最佳序列化代碼。序列化程序通常使用非常保守的技術(shù)編寫,因?yàn)樗鼈儽仨毴绱恕5牵绻喿x自己的序列化源代碼(使用序列化程序),您可以看到明顯的選擇應(yīng)該是什么,可以使序列化程序在您的特定情況下更加優(yōu)化。這正是這個(gè)新的源生成器所做的。

          除了提高性能和減少內(nèi)存之外,源代碼生成器還生成最適合裝配修整的代碼。這有助于制作更小的應(yīng)用程序。

          序列化POCO是一種非常常見的場(chǎng)景。使用新的源代碼生成器,我們觀察到序列化速度比我們的基準(zhǔn) 快1.6倍

          TechEmpower緩存基準(zhǔn)測(cè)試平臺(tái)或框架對(duì)來(lái)自數(shù)據(jù)庫(kù)的信息進(jìn)行內(nèi)存緩存。基準(zhǔn)測(cè)試的.NET 實(shí)現(xiàn)執(zhí)行緩存數(shù)據(jù)的JSON 序列化,以便將其作為響應(yīng)發(fā)送到測(cè)試工具。

          我們觀察到約100K RPS 增益( 增加約40%)。與 MemoryCache 性能改進(jìn)相結(jié)合時(shí),.NET 6 的吞吐量比.NET 5 高50% !

          C# 10

          歡迎來(lái)到C# 10。C# 10 的一個(gè)主要主題是繼續(xù)從C# 9 中的頂級(jí)語(yǔ)句開始的簡(jiǎn)化之旅。新功能從 Program.cs中刪除了更多的儀式,導(dǎo)致程序只有一行。 他們的靈感來(lái)自于與沒有 C# 經(jīng)驗(yàn)的人(學(xué)生、專業(yè)開發(fā)人員和其他人)交談,并了解什么對(duì)他們來(lái)說最有效且最直觀。

          大多數(shù).NET SDK 模板都已更新,以提供現(xiàn)在可以使用C# 10 實(shí)現(xiàn)的更簡(jiǎn)單、更簡(jiǎn)潔的體驗(yàn)。我們收到反饋說,有些人不喜歡新模板,因?yàn)樗鼈儾贿m合專家,刪除面向?qū)ο螅瑒h除在編寫C# 的第一天學(xué)習(xí)的重要概念,或鼓勵(lì)在一個(gè)文件中編寫整個(gè)程序。客觀地說,這些觀點(diǎn)都不正確。新模型同樣適用于作為專業(yè)開發(fā)人員的學(xué)生。但是,它與.NET 6 之前的C 派生模型不同。

          C# 10 中還有其他一些功能和改進(jìn),包括記錄結(jié)構(gòu)。

          全局使用指令

          全局using 指令讓您using只需指定一次指令并將其應(yīng)用于您編譯的每個(gè)文件。

          以下示例顯示了語(yǔ)法的廣度:

          • global using System;
          • global using static System.Console;
          • global using Env = System.Environment;

          您可以將global using語(yǔ)句放在任何 .cs 文件中,包括在 Program.cs中。

          隱式 usings 是一個(gè)MSBuild 概念,它會(huì)根據(jù)SDK自動(dòng)添加一組指令。例如,控制臺(tái)應(yīng)用程序隱式使用不同于ASP.NET Core。

          隱式使用是可選的,并在a 中啟用PropertyGroup

          • <ImplicitUsings>enable<mplicitUsings>

          隱式使用對(duì)于現(xiàn)有項(xiàng)目是可選的,但默認(rèn)包含在新C# 項(xiàng)目中。有關(guān)詳細(xì)信息,請(qǐng)參閱隱式使用。

          文件范圍的命名空間

          文件范圍的命名空間使您能夠聲明整個(gè)文件的命名空間,而無(wú)需將剩余內(nèi)容嵌套在{ ...}中. 只允許一個(gè),并且必須在聲明任何類型之前出現(xiàn)。

          新語(yǔ)法是單個(gè)的一行:

          namespace MyNamespace;
          
          class MyClass { ... } // Not indented

          這種新語(yǔ)法是三行縮進(jìn)樣式的替代方案:

          namespace MyNamespace
          {
              class MyClass { ... } // Everything is indented
          }

          好處是在整個(gè)文件位于同一個(gè)命名空間中的極其常見的情況下減少縮進(jìn)。

          記錄結(jié)構(gòu)

          C# 9 將記錄作為一種特殊的面向值的類形式引入。在C# 10 中,您還可以聲明結(jié)構(gòu)記錄。C# 中的結(jié)構(gòu)已經(jīng)具有值相等,但記錄結(jié)構(gòu)添加了==運(yùn)算符和IEquatable<T>的實(shí)現(xiàn),以及基于值的ToString實(shí)現(xiàn):

          public record struct Person
          {
              public string FirstName { get; init; }
              public string LastName { get; init; }
          }

          就像記錄類一樣,記錄結(jié)構(gòu)可以是"位置的",這意味著它們有一個(gè)主構(gòu)造函數(shù),它隱式聲明與參數(shù)對(duì)應(yīng)的公共成員:

          public record structPerson(stringFirstName,stringLastName);

          但是,與記錄類不同,隱式公共成員是_可變的自動(dòng)實(shí)現(xiàn)的屬性_。這樣一來(lái),記錄結(jié)構(gòu)就成為了元組的自然成長(zhǎng)故事。例如,如果您有一個(gè)返回類型(string FirstName, string LastName),并且您希望將其擴(kuò)展為命名類型,您可以輕松地聲明相應(yīng)的位置結(jié)構(gòu)記錄并維護(hù)可變語(yǔ)義。

          如果你想要一個(gè)具有只讀屬性的不可變記錄,你可以聲明整個(gè)記錄結(jié)構(gòu)readonly(就像你可以其他結(jié)構(gòu)一樣):

          publicreadonly record structPerson(stringFirstName,stringLastName);

          C# 10 不僅支持記錄結(jié)構(gòu),還支持_所有_結(jié)構(gòu)以及匿名類型的with表達(dá)式:

          var updatedPerson = person with{FirstName="Mary"};

          F# 6

          F# 6旨在讓F# 更簡(jiǎn)單、更高效。這適用于語(yǔ)言設(shè)計(jì)、庫(kù)和工具。我們對(duì)F# 6(及更高版本)的目標(biāo)是消除語(yǔ)言中讓用戶感到驚訝或阻礙學(xué)習(xí)F# 的極端情況。我們很高興能與F# 社區(qū)合作進(jìn)行這項(xiàng)持續(xù)的努力。

          讓 F# 更快、更互操作

          新語(yǔ)法task {…}直接創(chuàng)建一個(gè)任務(wù)并啟動(dòng)它。這是 F# 6 中最重要的功能之一,它使異步任務(wù)更簡(jiǎn)單、性能更高,并且與 C# 和其他 .NET 語(yǔ)言的互操作性更強(qiáng)。以前,創(chuàng)建 .NET 任務(wù)需要使用async {…}來(lái)創(chuàng)建任務(wù)并調(diào)用Async.StartImmediateAsTask

          該功能task {…}建立在稱為“可恢復(fù)代碼”RFC FS-1087的基礎(chǔ)之上。可恢復(fù)代碼是一個(gè)核心特性,我們希望在未來(lái)使用它來(lái)構(gòu)建其他高性能異步和屈服狀態(tài)機(jī)。

          F# 6 還為庫(kù)作者添加了其他性能特性,包括InlineIfLambda 和F#活動(dòng)模式的未裝箱表示。一個(gè)特別顯著的性能改進(jìn)在于列表和數(shù)組表達(dá)式的編譯,現(xiàn)在它們的速度提高了 4倍 ,并且調(diào)試也更好、更簡(jiǎn)單。

          讓 F# 更易學(xué)、更統(tǒng)一

          F# 6 啟用expr[idx]索引語(yǔ)法。到目前為止,F(xiàn)# 一直使用 expr.[idx] 進(jìn)行索引。刪除點(diǎn)符號(hào)是基于第一次使用 F# 用戶的反復(fù)反饋,點(diǎn)的使用與他們期望的標(biāo)準(zhǔn)實(shí)踐有不必要的差異。在新代碼中,我們建議系統(tǒng)地使用新的expr[idx]索引語(yǔ)法。作為一個(gè)社區(qū),我們都應(yīng)該切換到這種語(yǔ)法。

          F# 社區(qū)為使 F# 語(yǔ)言在 F# 6 中更加統(tǒng)一做出了重要改進(jìn)。其中最重要的是消除了 F# 縮進(jìn)規(guī)則中的一些不一致和限制。使 F# 更加統(tǒng)一的其他設(shè)計(jì)添加包括添加as圖案;在計(jì)算表達(dá)式中允許“重載自定義操作”(對(duì) DSL 有用);允許_丟棄use綁定并允許%B在輸出中進(jìn)行二進(jìn)制格式化。F# 核心庫(kù)添加了用于復(fù)制和更新列表、數(shù)組和序列的新函數(shù),以及其他NativePtr內(nèi)在函數(shù)。自 2.0 起棄用的 F# 的一些舊功能現(xiàn)在會(huì)導(dǎo)致錯(cuò)誤。其中許多更改更好地使 F# 與您的期望保持一致,從而減少意外。

          F# 6 還增加了對(duì) F# 中其他“隱式”和“類型導(dǎo)向”轉(zhuǎn)換的支持。這意味著更少的顯式向上轉(zhuǎn)換,并為 .NET 樣式的隱式轉(zhuǎn)換添加了一流的支持。F# 也進(jìn)行了調(diào)整,以更好地適應(yīng)使用 64 位整數(shù)的數(shù)字庫(kù)時(shí)代,并隱式擴(kuò)展了 32 位整數(shù)。

          改進(jìn) F# 工具

          F# 6 中的工具改進(jìn)使日常編碼更容易。新的"管道調(diào)試"允許您單步執(zhí)行、設(shè)置斷點(diǎn)并檢查 F# 管道語(yǔ)法input |> f1 |> f2 的中間值。陰影值的調(diào)試顯示已得到改進(jìn),消除了調(diào)試時(shí)常見的混淆源。F# 工具現(xiàn)在也更高效,F(xiàn)# 編譯器并行執(zhí)行解析階段。F# IDE 工具也得到了改進(jìn)。F# 腳本現(xiàn)在更加健壯,允許您通過global.json文件固定使用的 .NET SDK 版本。

          熱重載

          Hot Reload 是另一個(gè)性能特性,專注于開發(fā)人員的生產(chǎn)力。它使您能夠?qū)φ谶\(yùn)行的應(yīng)用程序進(jìn)行各種代碼編輯,從而縮短您等待應(yīng)用程序重新構(gòu)建、重新啟動(dòng)或重新導(dǎo)航到您在進(jìn)行代碼更改后所在位置所需的時(shí)間。

          Hot Reload 可通過dotnet watch CLI 工具和 Visual Studio 2022 使用。您可以將 Hot Reload 與多種應(yīng)用類型一起使用,例如 ASP.NET Core、Blazor、.NET MAUI、控制臺(tái)、Windows 窗體 (WinForms)、WPF、WinUI 3、Azure 函數(shù)等。

          使用 CLI 時(shí),只需使用 啟動(dòng)您的 .NET 6 應(yīng)用程序dotnet watch,進(jìn)行任何受支持的編輯,然后在保存文件時(shí)(如在 Visual Studio Code 中),這些更改將立即應(yīng)用。如果不支持更改,詳細(xì)信息將記錄到命令窗口。

          此圖像顯示了一個(gè)使用dotnet watch. 我對(duì).cs文件和.cshtml文件進(jìn)行了編輯(如日志中所述),兩者都應(yīng)用于代碼并在不到半秒的時(shí)間內(nèi)非常快速地反映在瀏覽器中。

          使用 Visual Studio 2022 時(shí),只需啟動(dòng)您的應(yīng)用程序,進(jìn)行支持的更改,然后使用新的"熱重載"按鈕(如下圖所示)應(yīng)用這些更改。您還可以通過同一按鈕上的下拉菜單選擇在保存時(shí)應(yīng)用更改。使用 Visual Studio 2022 時(shí),熱重載可用于多個(gè) .NET 版本,適用于 .NET 5+、.NET Core 和 .NET Framework。例如,您將能夠?qū)Π粹o的OnClickEvent處理程序進(jìn)行代碼隱藏更改。應(yīng)用程序的Main方法不支持它。

          注意:RuntimeInformation.FrameworkDescription中存在一個(gè)錯(cuò)誤,該錯(cuò)誤將在該圖像中展示,很快就會(huì)修復(fù)。

          Hot Reload 還與現(xiàn)有的 Edit and Continue 功能(在斷點(diǎn)處停止時(shí))以及用于實(shí)時(shí)編輯應(yīng)用程序 UI 的 XAML Hot Reload 協(xié)同工作。目前支持 C# 和 Visual Basic 應(yīng)用程序(不是 F#)。

          安全

          .NET 6 中的安全性得到了顯著改進(jìn)。它始終是團(tuán)隊(duì)關(guān)注的重點(diǎn),包括威脅建模、加密和深度防御防御。

          在 Linux 上,我們依賴OpenSSL進(jìn)行所有加密操作,包括 TLS(HTTPS 必需)。在 macOS 和 Windows 上,我們依賴操作系統(tǒng)提供的功能來(lái)實(shí)現(xiàn)相同的目的。對(duì)于每個(gè)新版本的 .NET,我們經(jīng)常需要添加對(duì)新版本 OpenSSL 的支持。.NET 6 增加了對(duì)OpenSSL 3的支持。

          OpenSSL 3 的最大變化是改進(jìn)的FIPS 140-2模塊和更簡(jiǎn)單的許可。

          .NET 6 需要 OpenSSL 1.1 或更高版本,并且會(huì)更喜歡它可以找到的最高安裝版本的 OpenSSL,直到并包括 v3。在一般情況下,當(dāng)您使用的 Linux 發(fā)行版默認(rèn)切換到 OpenSSL 3 時(shí),您最有可能開始使用 OpenSSL 3。大多數(shù)發(fā)行版還沒有這樣做。例如,如果您在 Red Hat 8 或 Ubuntu 20.04 上安裝 .NET 6,您將不會(huì)(在撰寫本文時(shí))開始使用 OpenSSL 3。

          OpenSSL 3、Windows 10 21H1 和 Windows Server 2022 都支持ChaCha20Poly1305。您可以在.NET 6 中使用這種新的經(jīng)過身份驗(yàn)證的加密方案(假設(shè)您的環(huán)境支持它)。

          感謝 Kevin Jones對(duì) ChaCha20Poly1305 的 Linux 支持。

          我們還發(fā)布了新的運(yùn)行時(shí)安全緩解路線圖。重要的是,您使用的運(yùn)行時(shí)不受教科書攻擊類型的影響。我們正在滿足這一需求。在 .NET 6 中,我們構(gòu)建了W^X和英特爾控制流強(qiáng)制技術(shù)(CET)的初始實(shí)現(xiàn)。W^X 完全受支持,默認(rèn)為 macOS Arm64 啟用,并且可以選擇加入其他環(huán)境。CET 是所有環(huán)境的選擇加入和預(yù)覽。我們希望在 .NET 7 中的所有環(huán)境中默認(rèn)啟用這兩種技術(shù)。

          Arm64

          這些天來(lái),對(duì)于筆記本電腦、云硬件和其他設(shè)備來(lái)說,Arm64 令人興奮不已。我們對(duì) .NET 團(tuán)隊(duì)感到同樣興奮,并正在盡最大努力跟上這一行業(yè)趨勢(shì)。我們直接與 Arm Holdings、Apple 和 Microsoft 的工程師合作,以確保我們的實(shí)施是正確和優(yōu)化的,并且我們的計(jì)劃保持一致。這些密切的合作伙伴關(guān)系對(duì)我們幫助很大。

          • 特別感謝 Apple 在 M1 芯片發(fā)布之前向我們的團(tuán)隊(duì)發(fā)送了一蒲式耳 Arm64 開發(fā)套件供我們使用,并提供了重要的技術(shù)支持。
          • 特別感謝 Arm Holdings,他們的工程師對(duì)我們的 Arm64 更改進(jìn)行了代碼審查,并進(jìn)行了性能改進(jìn)。

          在此之前,我們通過 .NET Core 3.0 和 Arm32 添加了對(duì) Arm64 的初始支持。該團(tuán)隊(duì)在最近的幾個(gè)版本中都對(duì) Arm64 進(jìn)行了重大投資,并且在可預(yù)見的未來(lái)這將繼續(xù)下去。在 .NET 6 中,我們主要關(guān)注在 macOS 和 Windows Arm64 操作系統(tǒng)上支持新的 Apple Silicon 芯片和x64 仿真場(chǎng)景。

          您可以在 macOS 11+ 和 Windows 11+ Arm64 操作系統(tǒng)上安裝 Arm64 和 x64 版本的 .NET。我們必須做出多種設(shè)計(jì)選擇和產(chǎn)品更改以確保其奏效。

          我們的策略是“親原生架構(gòu)”。我們建議您始終使用與原生架構(gòu)相匹配的 SDK,即 macOS 和 Windows Arm64 上的 Arm64 SDK。SDK 是大量的軟件。在 Arm64 芯片上本地運(yùn)行的性能將比仿真高得多。我們更新了 CLI 以簡(jiǎn)化操作。我們永遠(yuǎn)不會(huì)專注于優(yōu)化模擬 x64。

          默認(rèn)情況下,如果您dotnet run是帶有 Arm64 SDK 的 .NET 6 應(yīng)用程序,它將作為 Arm64 運(yùn)行。您可以使用參數(shù)輕松切換到以 x64 運(yùn)行,例如-adotnet run -a x64. 相同的論點(diǎn)適用于其他 CLI 動(dòng)詞。有關(guān)更多信息,請(qǐng)參閱 適用于macOS 和Windows Arm64 的.NET 6 RC2 更新。

          我想確保涵蓋其中的一個(gè)微妙之處。當(dāng)您使用-a x64時(shí),SDK 仍以 Arm64 方式原生運(yùn)行。.NET SDK 體系結(jié)構(gòu)中存在進(jìn)程邊界的固定點(diǎn)。在大多數(shù)情況下,一個(gè)進(jìn)程必須全是 Arm64 或全是 x64。我正在簡(jiǎn)化一點(diǎn),但 .NET CLI 會(huì)等待 SDK 架構(gòu)中的最后一個(gè)進(jìn)程創(chuàng)建,然后將其作為您請(qǐng)求的芯片架構(gòu)(如 x64)啟動(dòng)。這就是您的代碼運(yùn)行的過程。這樣,作為開發(fā)人員,您可以獲得 Arm64 的好處,但您的代碼可以在它需要的過程中運(yùn)行。這僅在您需要將某些代碼作為 x64 運(yùn)行時(shí)才相關(guān)。如果你不這樣做,那么你可以一直以 Arm64 的方式運(yùn)行所有東西,這很棒。

          Arm64支持

          對(duì)于 macOS 和 Windows Arm64,以下是您需要了解的要點(diǎn):

          • 支持并推薦 .NET 6 Arm64 和 x64 SDK。
          • 支持所有支持的 Arm64 和 x64 運(yùn)行時(shí)。
          • .NET Core 3.1 和 .NET 5 SDK 可以工作,但提供的功能較少,并且在某些情況下不受完全支持。
          • dotnet test尚未與 x64 仿真一起正常工作。我們正在努力。dotnet test將作為6.0.200 版本的一部分進(jìn)行改進(jìn),并且可能更早。

          有關(guān)更多完整信息,請(qǐng)參閱.NET 對(duì)macOS 和Windows Arm64的支持。

          此討論中缺少Linux。它不像macOS 和Windows 那樣支持x64 仿真。因此,這些新的CLI 特性和支持方法并不直接適用于Linux,Linux 也不需要它們。

          視窗Arm64

          我們有一個(gè)簡(jiǎn)單的工具來(lái)演示.NET 運(yùn)行的環(huán)境。

          C:Usersrich>dotnet tool install -g dotnet-runtimeinfo
          You can invoke the tool using the following command: dotnet-runtimeinfo
          Tool 'dotnet-runtimeinfo' (version '1.0.5') was successfully installed.
          
          C:Usersrich>dotnet runtimeinfo
                   42
                   42              ,d                             ,d
                   42              42                             42
           ,adPPYb,42  ,adPPYba, MM42MMM 8b,dPPYba,   ,adPPYba, MM42MMM
          a8"    `Y42 a8"     "8a  42    42P'   `"8a a8P_____42   42
          8b       42 8b       d8  42    42       42 8PP"""""""   42
          "8a,   ,d42 "8a,   ,a8"  42,   42       42 "8b,   ,aa   42,
           `"8bbdP"Y8  `"YbbdP"'   "Y428 42       42  `"Ybbd8"'   "Y428
          
          **.NET information
          Version: 6.0.0
          FrameworkDescription: .NET 6.0.0-rtm.21522.10
          Libraries version: 6.0.0-rtm.21522.10
          Libraries hash: 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6
          
          **Environment information
          ProcessorCount: 8
          OSArchitecture: Arm64
          OSDescription: Microsoft Windows 10.0.22494
          OSVersion: Microsoft Windows NT 10.0.22494.0

          如您所見,該工具在Windows Arm64 上本機(jī)運(yùn)行。我將向您展示http://ASP.NET Core 的樣子。

          macOS Arm64

          您可以看到在macOS Arm64 上的體驗(yàn)是相似的,并且還展示了架構(gòu)目標(biāo)。

          rich@MacBook-Air app % dotnet --version
          6.0.100
          rich@MacBook-Air app % dotnet --info | grep RID
           RID:         osx-arm64
          rich@MacBook-Air app % cat Program.cs 
          using System.Runtime.InteropServices;
          using static System.Console;
          
          WriteLine($"Hello, {RuntimeInformation.OSArchitecture} from {RuntimeInformation.FrameworkDescription}!");
          rich@MacBook-Air app % dotnet run
          Hello, Arm64 from .NET 6.0.0-rtm.21522.10!
          rich@MacBook-Air app % dotnet run -a x64
          Hello, X64 from .NET 6.0.0-rtm.21522.10!
          rich@MacBook-Air app % 

          這張圖片展示了Arm64 執(zhí)行是Arm64 SDK 的默認(rèn)設(shè)置,以及使用-a參數(shù)在目標(biāo)Arm64 和x64 之間切換是多么容易。完全相同的體驗(yàn)適用于Windows Arm64。

          此圖像演示了相同的內(nèi)容,但使用的是http://ASP.NET Core。我正在使用與您在上圖中看到的相同的.NET 6 Arm64 SDK。

          Arm64 上的 Docker

          Docker 支持在本機(jī)架構(gòu)和仿真中運(yùn)行的容器,本機(jī)架構(gòu)是默認(rèn)的。這看起來(lái)很明顯,但當(dāng)大多數(shù)Docker Hub 目錄都是面向x64 時(shí),這可能會(huì)讓人感到困惑。您可以使用-platform linux/amd64來(lái)請(qǐng)求x64 圖像。

          我們僅支持在Arm64 操作系統(tǒng)上運(yùn)行Linux Arm64 .NET 容器映像。這是因?yàn)槲覀儚牟恢С衷赒EMU中運(yùn)行.NET ,這是Docker 用于架構(gòu)模擬的。看來(lái)這可能是由于 QEMU 的限制。


          此圖像演示了我們維護(hù)的控制臺(tái)示例:mcr.microsoft.com/dotnet/samples。 這是一個(gè)有趣的示例,因?yàn)樗恍┗具壿嫞糜诖蛴∧梢允褂玫腃PU 和內(nèi)存限制信息。我展示的圖像設(shè)置了CPU 和內(nèi)存限制。

          自己試試吧:docker run --rm mcr.microsoft.com/dotnet/samples

          Arm64 性能

          Apple Silicon 和x64 仿真支持項(xiàng)目非常重要,但是,我們也普遍提高了Arm64 性能。

          此圖像演示了將堆棧幀的內(nèi)容清零的改進(jìn),這是一種常見的操作。綠線是新行為,而橙色線是另一個(gè)(不太有益的)實(shí)驗(yàn),兩者都相對(duì)于基線有所改善,由藍(lán)線表示。對(duì)于此測(cè)試,越低越好。

          容器

          .NET 6 更適合容器,主要基于本文中討論的所有改進(jìn),適用于Arm64 和x64。我們還進(jìn)行了有助于各種場(chǎng)景的關(guān)鍵更改。使用.NET 6 驗(yàn)證容器改進(jìn)演示了其中一些改進(jìn)正在一起測(cè)試。

          Windows 容器改進(jìn)和新環(huán)境變量也包含在11 月9 日(明天)發(fā)布的11 月.NET Framework 4.8 容器更新中。

          發(fā)布說明可在我們的docker 存儲(chǔ)庫(kù)中找到:

          • .NET 6 容器發(fā)行說明
          • .NET Framework 4.8 2021 年11 月容器發(fā)行說明

          Windows 容器

          .NET 6 增加了對(duì)Windows 進(jìn)程隔離容器的支持。如果您在 Azure Kubernetes 服務(wù)(AKS) 中使用Windows 容器,那么您依賴于進(jìn)程隔離的容器。進(jìn)程隔離容器可以被認(rèn)為與Linux 容器非常相似。Linux 容器使用cgroups,Windows 進(jìn)程隔離容器使用Job Objects。Windows 還提供Hyper-V 容器,通過更強(qiáng)大的虛擬化提供更大的隔離。Hyper-V 容器的.NET 6 沒有任何變化。

          此更改的主要價(jià)值是現(xiàn)在Environment.ProcessorCount將使用Windows 進(jìn)程隔離容器報(bào)告正確的值。如果在64 核機(jī)器上創(chuàng)建2 核容器,Environment.ProcessorCount將返回2. 在以前的版本中,此屬性將報(bào)告機(jī)器上的處理器總數(shù),與Docker CLI、Kubernetes 或其他容器編排器/運(yùn)行時(shí)指定的限制無(wú)關(guān)。此值被.NET 的各個(gè)部分用于擴(kuò)展目的,包括.NET 垃圾收集器(盡管它依賴于相關(guān)的較低級(jí)別的API)。社區(qū)庫(kù)也依賴此API 進(jìn)行擴(kuò)展。

          我們最近在AKS 上使用大量pod 在生產(chǎn)中的Windows 容器上與客戶驗(yàn)證了這一新功能。他們能夠以50% 的內(nèi)存(與他們的典型配置相比)成功運(yùn)行,這是以前導(dǎo)致異常的OutOfMemoryException水平StackOverflowException。他們沒有花時(shí)間找到最低內(nèi)存配置,但我們猜測(cè)它明顯低于他們典型內(nèi)存配置的50%。由于這一變化,他們將轉(zhuǎn)向更便宜的Azure 配置,從而節(jié)省資金。只需升級(jí)即可,這是一個(gè)不錯(cuò)的、輕松的勝利。

          優(yōu)化縮放

          我們從用戶那里聽說,某些應(yīng)用程序在Environment.ProcessorCount報(bào)告正確的值時(shí)無(wú)法實(shí)現(xiàn)最佳擴(kuò)展。如果這聽起來(lái)與您剛剛閱讀的有關(guān)Windows 容器的內(nèi)容相反,那么它有點(diǎn)像。.NET 6 現(xiàn)在提供DOTNET_PROCESSOR_COUNT 環(huán)境變量來(lái)手動(dòng)控制Environment.ProcessorCount的值。在典型的用例中,應(yīng)用程序可能在64 核機(jī)器上配置為4核,并且在8或16核方面擴(kuò)展得最好。此環(huán)境變量可用于啟用該縮放。

          這個(gè)模型可能看起來(lái)很奇怪,其中Environment.ProcessorCount--cpus(通過Docker CLI)值可能不同。默認(rèn)情況下,容器運(yùn)行時(shí)面向核心等價(jià)物,而不是實(shí)際核心。這意味著,當(dāng)你說你想要4 個(gè)核心時(shí),你得到的CPU 時(shí)間與4 個(gè)核心相當(dāng),但你的應(yīng)用程序可能(理論上)在更多的核心上運(yùn)行,甚至在短時(shí)間內(nèi)在64 核機(jī)器上運(yùn)行所有64 個(gè)核心。這可能使您的應(yīng)用程序能夠在超過4 個(gè)線程上更好地?cái)U(kuò)展(繼續(xù)示例),并且分配更多可能是有益的。這假定線程分配基于 Environment.ProcessorCount的值。如果您選擇設(shè)置更高的值,您的應(yīng)用程序可能會(huì)使用更多內(nèi)存。對(duì)于某些工作負(fù)載,這是一個(gè)簡(jiǎn)單的權(quán)衡。至少,這是一個(gè)您可以測(cè)試的新選項(xiàng)。

          Linux 和Windows 容器均支持此新功能。

          Docker 還提供了一個(gè)CPU 組功能,您的應(yīng)用程序可以關(guān)聯(lián)到特定的內(nèi)核。在這種情況下不建議使用此功能,因?yàn)閼?yīng)用程序可以訪問的內(nèi)核數(shù)量是具體定義的。我們還看到了將它與Hyper-V 容器一起使用時(shí)的一些問題,并且它并不是真正適用于那種隔離模式。

          Debian 11 "bullseye"

          我們密切關(guān)注Linux 發(fā)行版的生命周期和發(fā)布計(jì)劃,并嘗試代表您做出最佳選擇。Debian 是我們用于默認(rèn)Linux 映像的Linux 發(fā)行版。如果您6.0從我們的一個(gè)容器存儲(chǔ)庫(kù)中提取標(biāo)簽,您將提取一個(gè)Debian 映像(假設(shè)您使用的是Linux 容器)。對(duì)于每個(gè)新的.NET 版本,我們都會(huì)考慮是否應(yīng)該采用新的Debian 版本。

          作為一項(xiàng)政策,我們不會(huì)為了方便標(biāo)簽而更改Debian 版本,例如6.0, mid-release。如果我們這樣做了,某些應(yīng)用程序肯定會(huì)崩潰。這意味著,在發(fā)布開始時(shí)選擇Debian 版本非常重要。此外,這些圖像得到了很多使用,主要是因?yàn)樗鼈兪?#34;好標(biāo)簽"的引用。

          Debian 和.NET 版本自然不會(huì)一起計(jì)劃。當(dāng)我們開始.NET 6 時(shí),我們看到Debian "bullseye" 可能會(huì)在2021 年發(fā)布。我們決定從發(fā)布開始就押注于Bullseye。我們開始使用.NET 6 Preview 1發(fā)布基于靶心的容器映像,并決定不再回頭。賭注是.NET 6 版本會(huì)輸?shù)襞c靶心版本的競(jìng)爭(zhēng)。到8 月8 日,我們?nèi)匀徊恢繠ullseye 什么時(shí)候發(fā)貨,距離我們自己的版本發(fā)布還有三個(gè)月,即11 月8 日。我們不想在預(yù)覽版Linux 上發(fā)布生產(chǎn).NET 6,但我們堅(jiān)持我們會(huì)輸?shù)暨@場(chǎng)競(jìng)賽的計(jì)劃很晚。

          當(dāng)Debian 11 "bullseye"于8 月14 日發(fā)布時(shí),我們感到非常驚喜。我們輸?shù)袅吮荣悾A得了賭注。這意味著默認(rèn)情況下,.NET 6 用戶從第一天開始就可以獲得最佳和最新的Debian。我們相信Debian 11 和.NET 6 將是許多用戶的絕佳組合。抱歉,克星,我們中了靶心。

          較新的發(fā)行版在其軟件包提要中包含各種軟件包的較新主要版本,并且通常可以更快地獲得CVE 修復(fù)。這是對(duì)較新內(nèi)核的補(bǔ)充。新發(fā)行版可以更好地為用戶服務(wù)。

          再往前看,我們很快就會(huì)開始計(jì)劃對(duì)Ubuntu 22.04的支持。Ubuntu是另一個(gè)Debian 系列發(fā)行版,深受.NET 開發(fā)人員的歡迎。我們希望為新的Ubuntu LTS 版本提供當(dāng)日支持。

          向Tianon Gravi 致敬,感謝他們?yōu)樯鐓^(qū)維護(hù)Debian 映像并在我們有問題時(shí)幫助我們。

          Dotnet Monitor

          dotnet monitor是容器的重要診斷工具。它作為 sidecar 容器鏡像已經(jīng)有一段時(shí)間了,但處于不受支持的"實(shí)驗(yàn)"狀態(tài)。作為.NET 6 的一部分,我們正在發(fā)布一個(gè)基于.NET 6 的dotnet monitor映像,該映像在生產(chǎn)中得到完全支持。

          dotnet monitor已被Azure App Service 用作其http://ASP.NET Core Linux 診斷體驗(yàn)的實(shí)現(xiàn)細(xì)節(jié)。這是預(yù)期的場(chǎng)景之一,建立在dotnet monitor 之上,以提供更高級(jí)別和更高價(jià)值的體驗(yàn)。

          您現(xiàn)在可以拉取新圖像:

          docker pull mcr.microsoft.com/dotnet/monitor:6.0

          dotnet monitor使從.NET 進(jìn)程訪問診斷信息(日志、跟蹤、進(jìn)程轉(zhuǎn)儲(chǔ))變得更加容易。在臺(tái)式機(jī)上訪問所需的所有診斷信息很容易,但是,這些熟悉的技術(shù)在使用容器的生產(chǎn)環(huán)境中可能不起作用。dotnet monitor提供了一種統(tǒng)一的方式來(lái)收集這些診斷工件,無(wú)論是在您的桌面計(jì)算機(jī)上還是在Kubernetes 集群中運(yùn)行。收集這些診斷工件有兩種不同的機(jī)制:

          • 用于臨時(shí)收集工件的 HTTP API。當(dāng)您已經(jīng)知道您的應(yīng)用程序遇到問題并且您有興趣收集更多信息時(shí),您可以調(diào)用這些API 端點(diǎn)。
          • 基于規(guī)則的配置 觸發(fā)器,用于始終在線收集工件。您可以配置規(guī)則以在滿足所需條件時(shí)收集診斷數(shù)據(jù),例如,當(dāng)您持續(xù)高CPU 時(shí)收集進(jìn)程轉(zhuǎn)儲(chǔ)。

          dotnet monitor為.NET 應(yīng)用程序提供了一個(gè)通用的診斷API,可以使用任何工具在任何地方工作。“通用API”不是.NET API,而是您可以調(diào)用和查詢的Web API。dotnet monitor包括一個(gè)ASP.NET Web 服務(wù)器,它直接與.NET 運(yùn)行時(shí)中的診斷服務(wù)器交互并公開來(lái)自診斷服務(wù)器的數(shù)據(jù)設(shè)計(jì)dotnet monitor可實(shí)現(xiàn)生產(chǎn)中的高性能監(jiān)控和安全使用,以控制對(duì)特權(quán)信息的訪問。dotnet monitor通過非Internet 可尋址的unix domain socket與運(yùn)行時(shí)交互——跨越容器邊界。該模型通信模型非常適合此用例。

          結(jié)構(gòu)化 JSON 日志

          JSON 格式化程序現(xiàn)在是aspnet.NET 6 容器映像中的默認(rèn)控制臺(tái)記錄器。.NET 5 中的默認(rèn)設(shè)置為簡(jiǎn)單的控制臺(tái)格式化程序。進(jìn)行此更改是為了使默認(rèn)配置與依賴機(jī)器可讀格式(如JSON)的自動(dòng)化工具一起使用。

          圖像的輸出現(xiàn)在如下所示aspnet

          $ docker run --rm -it -p 8000:80 mcr.microsoft.com/dotnet/samples:aspnetapp
          {"EventId":60,"LogLevel":"Warning","Category":"Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository","Message":"Storing keys in a directory u0027/root/.aspnet/DataProtection-Keysu0027 that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.","State":{"Message":"Storing keys in a directory u0027/root/.aspnet/DataProtection-Keysu0027 that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.","path":"/root/.aspnet/DataProtection-Keys","{OriginalFormat}":"Storing keys in a directory u0027{path}u0027 that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed."}}
          {"EventId":35,"LogLevel":"Warning","Category":"Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager","Message":"No XML encryptor configured. Key {86cafacf-ab57-434a-b09c-66a929ae4fd7} may be persisted to storage in unencrypted form.","State":{"Message":"No XML encryptor configured. Key {86cafacf-ab57-434a-b09c-66a929ae4fd7} may be persisted to storage in unencrypted form.","KeyId":"86cafacf-ab57-434a-b09c-66a929ae4fd7","{OriginalFormat}":"No XML encryptor configured. Key {KeyId:B} may be persisted to storage in unencrypted form."}}
          {"EventId":14,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Now listening on: http://[::]:80","State":{"Message":"Now listening on: http://[::]:80","address":"http://[::]:80","{OriginalFormat}":"Now listening on: {address}"}}
          {"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Application started. Press Ctrlu002BC to shut down.","State":{"Message":"Application started. Press Ctrlu002BC to shut down.","{OriginalFormat}":"Application started. Press Ctrlu002BC to shut down."}}
          {"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Hosting environment: Production","State":{"Message":"Hosting environment: Production","envName":"Production","{OriginalFormat}":"Hosting environment: {envName}"}}
          {"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Content root path: /app","State":{"Message":"Content root path: /app","contentRoot":"/app","{OriginalFormat}":"Content root path: {contentRoot}"}}

          Logging\_\_Console\_\_FormatterName可以通過設(shè)置或取消設(shè)置環(huán)境變量或通過代碼更改來(lái)更改記錄器格式類型(有關(guān)更多詳細(xì)信息,請(qǐng)參閱控制臺(tái)日志格式)。

          更改后,您將看到如下輸出(就像.NET 5 一樣):

          $ docker run --rm -it -p 8000:80 -e Logging__Console__FormatterName="" mcr.microsoft.com/dotnet/samples:aspnetapp
          warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
                Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
          warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
                No XML encryptor configured. Key {8d4ddd1d-ccfc-4898-9fe1-3e7403bf23a0} may be persisted to storage in unencrypted form.
          info: Microsoft.Hosting.Lifetime[14]
                Now listening on: http://[::]:80
          info: Microsoft.Hosting.Lifetime[0]
                Application started. Press Ctrl+C to shut down.
          info: Microsoft.Hosting.Lifetime[0]
                Hosting environment: Production
          info: Microsoft.Hosting.Lifetime[0]
                Content root path: /app

          注意:此更改不會(huì)影響開發(fā)人員計(jì)算機(jī)上的.NET SDK,例如dotnet run.此更改特定于aspnet容器映像。

          支持 OpenTelemetry 指標(biāo)

          作為我們關(guān)注可觀察性的一部分,我們一直在為最后幾個(gè).NET 版本添加對(duì) OpenTelemetry 的支持。在.NET 6 中,我們添加了對(duì)OpenTelemetry Metrics API的支持。通過添加對(duì)OpenTelemetry 的支持,您的應(yīng)用程序可以與其他OpenTelemetry系統(tǒng)無(wú)縫互操作。

          System.Diagnostics.Metrics是OpenTelemetry Metrics API 規(guī)范的.NET 實(shí)現(xiàn)。Metrics API 是專門為處理原始測(cè)量而設(shè)計(jì)的,目的是高效、同時(shí)地生成這些測(cè)量的連續(xù)摘要。

          API 包括Meter可用于創(chuàng)建儀器對(duì)象的類。API 公開了四個(gè)工具類:CounterHistogramObservableCounter和,ObservableGauge以支持不同的度量方案。此外,API 公開MeterListener該類以允許收聽儀器記錄的測(cè)量值,以用于聚合和分組目的。

          OpenTelemetry .NET 實(shí)現(xiàn)將被擴(kuò)展以使用這些新的API,這些API 添加了對(duì)Metrics 可觀察性場(chǎng)景的支持。

          圖書館測(cè)量記錄示例

               Meter meter = new Meter("io.opentelemetry.contrib.mongodb", "v1.0");
              Counter<int> counter = meter.CreateCounter<int>("Requests");
              counter.Add(1);
              counter.Add(1, KeyValuePair.Create<string, object>("request", "read"));

          聽力示例

            MeterListener listener = new MeterListener();
              listener.InstrumentPublished = (instrument, meterListener) =>
              {
                  if (instrument.Name == "Requests" && instrument.Meter.Name == "io.opentelemetry.contrib.mongodb")
                  {
                      meterListener.EnableMeasurementEvents(instrument, null);
                  }
              };
              listener.SetMeasurementEventCallback<int>((instrument, measurement, tags, state) =>
              {
                  Console.WriteLine($"Instrument: {instrument.Name} has recorded the measurement {measurement}");
              });
              listener.Start();

          Windows Forms

          我們繼續(xù)在 Windows 窗體中進(jìn)行重要改進(jìn)。.NET 6 包括更好的控件可訪問性、設(shè)置應(yīng)用程序范圍的默認(rèn)字體、模板更新等的能力。

          可訪問性改進(jìn)

          在此版本中,我們添加了用于CheckedListBoxLinkLabelPanelScrollBarTabControlTrackBar的UIA 提供程序,它們使講述人等工具和測(cè)試自動(dòng)化能夠與應(yīng)用程序的元素進(jìn)行交互。

          默認(rèn)字體

          您現(xiàn)在可以使用.Application.SetDefaultFont

          voidApplication.SetDefaultFont(Font font)

          最小的應(yīng)用程序

          以下是帶有 .NET 6 的最小Windows 窗體應(yīng)用程序:

          class Program
          {
              [STAThread]
              static void Main()
              {
                  ApplicationConfiguration.Initialize();
                  Application.Run(new Form1());
              }
          }

          作為.NET 6 版本的一部分,我們一直在更新大多數(shù)模板,使其更加現(xiàn)代和簡(jiǎn)約,包括Windows 窗體。我們決定讓W(xué)indows 窗體模板更傳統(tǒng)一些,部分原因是需要將[STAThread]屬性應(yīng)用于應(yīng)用程序入口點(diǎn)。然而,還有更多的戲劇而不是立即出現(xiàn)在眼前。

          ApplicationConfiguration.Initialize()是一個(gè)源生成API,它在后臺(tái)發(fā)出以下調(diào)用:

          Application.EnableVisualStyles();
          Application.SetCompatibleTextRenderingDefault(false);
          Application.SetDefaultFont(new Font(...));
          Application.SetHighDpiMode(HighDpiMode.SystemAware);

          這些調(diào)用的參數(shù)可通過csproj 或props 文件中的MSBuild 屬性進(jìn)行配置。

          Visual Studio 2022 中的Windows 窗體設(shè)計(jì)器也知道這些屬性(目前它只讀取默認(rèn)字體),并且可以向您顯示您的應(yīng)用程序,就像它在運(yùn)行時(shí)一樣:

          模板更新

          C# 的Windows 窗體模板已更新,以支持新的應(yīng)用程序引導(dǎo)、global using指令、文件范圍的命名空間和可為空的引用類型。

          更多運(yùn)行時(shí) designers

          現(xiàn)在您可以構(gòu)建通用設(shè)計(jì)器(例如,報(bào)表設(shè)計(jì)器),因?yàn)?NET 6 具有設(shè)計(jì)器和與設(shè)計(jì)器相關(guān)的基礎(chǔ)架構(gòu)所缺少的所有部分。有關(guān)詳細(xì)信息,請(qǐng)參閱此博客文章。

          單文件應(yīng)用

          在.NET 6中,已為 Windows 和macOS 啟用內(nèi)存中單文件應(yīng)用程序。在.NET 5 中,這種部署類型僅限于 Linux。您現(xiàn)在可以為所有受支持的操作系統(tǒng)發(fā)布作為單個(gè)文件部署和啟動(dòng)的單文件二進(jìn)制文件。單文件應(yīng)用不再將任何核心運(yùn)行時(shí)程序集提取到臨時(shí)目錄。

          這種擴(kuò)展功能基于稱為"超級(jí)主機(jī)"的構(gòu)建塊。"apphost" 是在非單文件情況下啟動(dòng)應(yīng)用程序的可執(zhí)行文件,例如myapp.exe./myapp. Apphost 包含用于查找運(yùn)行時(shí)、加載它并使用該運(yùn)行時(shí)啟動(dòng)您的應(yīng)用程序的代碼。Superhost 仍然執(zhí)行其中一些任務(wù),但使用所有CoreCLR 本機(jī)二進(jìn)制文件的靜態(tài)鏈接副本。靜態(tài)鏈接是我們用來(lái)實(shí)現(xiàn)單一文件體驗(yàn)的方法。本機(jī)依賴項(xiàng)(如NuGet 包附帶的)是單文件嵌入的顯著例外。默認(rèn)情況下,它們不包含在單個(gè)文件中。例如,WPF 本機(jī)依賴項(xiàng)不是超級(jí)主機(jī)的一部分,因此會(huì)在單文件應(yīng)用程序之外產(chǎn)生其他文件。您可以使用該設(shè)置IncludeNativeLibrariesForSelfExtract嵌入和提取本機(jī)依賴項(xiàng)。

          靜態(tài)分析

          我們改進(jìn)了單文件分析器以允許自定義警告。如果您的API 在單文件發(fā)布中不起作用,您現(xiàn)在可以使用[RequiresAssemblyFiles]屬性對(duì)其進(jìn)行標(biāo)記,如果啟用了分析器,則會(huì)出現(xiàn)警告。添加該屬性還將使方法中與單個(gè)文件相關(guān)的所有警告靜音,因此您可以使用該警告將警告向上傳播到您的公共API。

          當(dāng) PublishSingleFile 設(shè)置為true 時(shí),會(huì)自動(dòng)為exe 項(xiàng)目啟用單文件分析器,但您也可以通過將 EnableSingleFileAnalysis 設(shè)置為true 來(lái)為任何項(xiàng)目啟用它。 如果您想支持將庫(kù)作為單個(gè)文件應(yīng)用程序的一部分,這將很有幫助。

          在.NET 5 中,我們?yōu)閱挝募行袨椴煌?/span>Assembly.Location和一些其他API添加了警告。

          壓縮

          單文件包現(xiàn)在支持壓縮,可以通過將屬性設(shè)置EnableCompressionInSingleFiletrue. 在運(yùn)行時(shí),文件會(huì)根據(jù)需要解壓縮到內(nèi)存中。壓縮可以為某些場(chǎng)景節(jié)省大量空間。

          讓我們看一下與NuGet 包資源管理器一起使用的單個(gè)文件發(fā)布(帶壓縮和不帶壓縮)。

          無(wú)壓縮: 172 MB

          壓縮: 71.6 MB

          壓縮會(huì)顯著增加應(yīng)用程序的啟動(dòng)時(shí)間,尤其是在Unix 平臺(tái)上。Unix 平臺(tái)有一個(gè)不能用于壓縮的無(wú)拷貝快速啟動(dòng)路徑。您應(yīng)該在啟用壓縮后測(cè)試您的應(yīng)用程序,看看額外的啟動(dòng)成本是否可以接受。

          單文件調(diào)試

          目前只能使用平臺(tái)調(diào)試器(如WinDBG)來(lái)調(diào)試單文件應(yīng)用程序。我們正在考慮使用更高版本的Visual Studio 2022 添加Visual Studio 調(diào)試。

          macOS 上的單文件簽名

          單文件應(yīng)用程序現(xiàn)在滿足macOS 上的Apple 公證和簽名要求。具體更改與我們根據(jù)離散文件布局構(gòu)建單文件應(yīng)用程序的方式有關(guān)。

          Apple 開始對(duì)macOS Catalina 實(shí)施新的簽名和公證要求。我們一直在與Apple 密切合作,以了解需求,并尋找使.NET 等開發(fā)平臺(tái)能夠在該環(huán)境中正常工作的解決方案。我們已經(jīng)進(jìn)行了產(chǎn)品更改并記錄了用戶工作流程,以滿足Apple 在最近幾個(gè).NET 版本中的要求。剩下的差距之一是單文件簽名,這是在macOS 上分發(fā).NET 應(yīng)用程序的要求,包括在macOS 商店中。

          IL 修整

          該團(tuán)隊(duì)一直致力于為多個(gè)版本進(jìn)行IL 修整。.NET 6 代表了這一旅程向前邁出的重要一步。我們一直在努力使更激進(jìn)的修剪模式安全且可預(yù)測(cè),因此有信心將其設(shè)為默認(rèn)模式。TrimMode=link以前是可選功能,現(xiàn)在是默認(rèn)功能。

          我們有一個(gè)三管齊下的修剪策略:

          • 提高平臺(tái)的修剪能力。
          • 對(duì)平臺(tái)進(jìn)行注釋以提供更好的警告并使其他人也能這樣做。
          • 在此基礎(chǔ)上,讓默認(rèn)的修剪模式更具侵略性,以便讓應(yīng)用程序變小。

          由于使用未注釋反射的應(yīng)用程序的結(jié)果不可靠,修剪之前一直處于預(yù)覽狀態(tài)。有了修剪警告,體驗(yàn)現(xiàn)在應(yīng)該是可預(yù)測(cè)的。沒有修剪警告的應(yīng)用程序應(yīng)該正確修剪并且在運(yùn)行時(shí)觀察到行為沒有變化。目前,只有核心的.NET 庫(kù)已經(jīng)完全注解了修剪,但我們希望看到生態(tài)系統(tǒng)注釋修剪并兼容修剪

          減小應(yīng)用程序大小

          讓我們使用SDK 工具之一的crossgen來(lái)看看這個(gè)修剪改進(jìn)。它可以通過幾個(gè)修剪警告進(jìn)行修剪,crossgen 團(tuán)隊(duì)能夠解決。

          首先,讓我們看一下將crossgen 發(fā)布為一個(gè)獨(dú)立的應(yīng)用程序而無(wú)需修剪。它是80 MB(包括.NET 運(yùn)行時(shí)和所有庫(kù))。

          然后我們可以嘗試(現(xiàn)在是舊版).NET 5 默認(rèn)修剪模式,copyused. 結(jié)果降至55 MB。

          新的.NET 6 默認(rèn)修剪模式link將獨(dú)立文件大小進(jìn)一步降低到36MB。

          我們希望新的link修剪模式能更好地與修剪的期望保持一致:顯著節(jié)省和可預(yù)測(cè)的結(jié)果。

          默認(rèn)啟用警告

          修剪警告告訴您修剪可能會(huì)刪除運(yùn)行時(shí)使用的代碼的地方。這些警告以前默認(rèn)禁用,因?yàn)榫娣浅`须s,主要是由于 .NET 平臺(tái)沒有參與修剪作為第一類場(chǎng)景。

          我們對(duì)大部分 .NET 庫(kù)進(jìn)行了注釋,以便它們產(chǎn)生準(zhǔn)確的修剪警告。因此,我們覺得是時(shí)候默認(rèn)啟用修剪警告了。http://ASP.NET Core 和 Windows 桌面運(yùn)行時(shí)庫(kù)尚未注釋。我們計(jì)劃接下來(lái)注釋 http://ASP.NET 服務(wù)組件(在 .NET 6 之后)。我們希望看到社區(qū)在 .NET 6 發(fā)布后對(duì) NuGet 庫(kù)進(jìn)行注釋。

          您可以通過設(shè)置<SuppressTrimAnalysisWarnings>true來(lái)禁用警告。

          更多信息:

          • 修剪警告
          • 修剪介紹
          • 準(zhǔn)備.NET 庫(kù)以進(jìn)行修剪

          與本機(jī) AOT 共享

          我們也為Native AOT實(shí)驗(yàn)實(shí)現(xiàn)了相同的修剪警告,這應(yīng)該會(huì)以幾乎相同的方式改善 Native AOT 編譯體驗(yàn)。

          數(shù)學(xué)

          我們顯著改進(jìn)了數(shù)學(xué) API。社區(qū)中的一些人已經(jīng)在享受這些改進(jìn)。

          面向性能的 API

          System.Math 中添加了面向性能的數(shù)學(xué) API。如果底層硬件支持,它們的實(shí)現(xiàn)是硬件加速的。

          新 API:

          • SinCos用于同時(shí)計(jì)算SinCos
          • ReciprocalEstimate用于計(jì)算 1 / x的近似值。
          • ReciprocalSqrtEstimate用于計(jì)算1 / Sqrt(x)的近似值。

          新的重載:

          • Clamp, DivRem,MinMax支持nintnuint
          • AbsSign支持nint
          • DivRem 變體返回tuple

          性能改進(jìn):

          • ScaleB被移植到C# 導(dǎo)致調(diào)用速度提高了93%。感謝亞歷克斯·科文頓。

          大整數(shù)性能

          改進(jìn)了從十進(jìn)制和十六進(jìn)制字符串中解析 BigIntegers。我們看到了高達(dá)89% 的改進(jìn),如下圖所示(越低越好)。

          感謝約瑟夫·達(dá)席爾瓦。

          Complex API 現(xiàn)在注釋為 readonly

          現(xiàn)在對(duì)各種API 進(jìn)行了注釋,System.Numerics.Complexreadonly以確保不會(huì)對(duì)readonly值或傳遞的值進(jìn)行復(fù)制in。

          歸功于hrrrrustic 。

          BitConverter 現(xiàn)在支持浮點(diǎn)到無(wú)符號(hào)整數(shù)位廣播

          BitConverter 現(xiàn)在支持DoubleToUInt64Bits, HalfToUInt16Bits, SingleToUInt32Bits, UInt16BitsToHalf, UInt32BitsToSingle, 和UInt64BitsToDouble. 這應(yīng)該使得在需要時(shí)更容易進(jìn)行浮點(diǎn)位操作。

          歸功于Michal Petryka 。

          BitOperations 支持附加功能

          BitOperations現(xiàn)在支持IsPow2,RoundUpToPowerOf2和提供nint/nuint重載現(xiàn)有函數(shù)。

          感謝約翰凱利、霍耀源和羅賓林德納。

          Vector, Vector2, Vector3 和 Vector4 改進(jìn)

          Vector現(xiàn)在支持C# 9 中添加的原始類型nint和nuint原始類型。例如,此更改應(yīng)該可以更簡(jiǎn)單地使用帶有指針或平臺(tái)相關(guān)長(zhǎng)度類型的SIMD 指令。

          Vector現(xiàn)在支持一種Sum方法來(lái)簡(jiǎn)化計(jì)算向量中所有元素的“水平和”的需要。歸功于伊萬(wàn)茲拉塔諾夫。

          Vector現(xiàn)在支持一種通用方法As<TFrom, TTo>來(lái)簡(jiǎn)化在具體類型未知的通用上下文中處理向量。感謝霍耀源

          重載支持Span已添加到Vector2Vector3Vector4以改善需要加載或存儲(chǔ)矢量類型時(shí)的體驗(yàn)。

          更好地解析標(biāo)準(zhǔn)數(shù)字格式

          我們改進(jìn)了標(biāo)準(zhǔn)數(shù)字類型的解析器,特別是.ToString.TryFormatParse。他們現(xiàn)在將理解對(duì)精度 >99 位小數(shù)的要求,并將為那么多位數(shù)提供準(zhǔn)確的結(jié)果。此外,解析器現(xiàn)在更好地支持方法中的尾隨零。

          以下示例演示了之前和之后的行為。

          • 32.ToString("C100")->C132
            • .NET 6:.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
            • .NET 5:我們?cè)诟袷交a中人為限制只能處理 <= 99 的精度。對(duì)于精度 >= 100,我們改為將輸入解釋為自定義格式。
          • 32.ToString("H99")->扔一個(gè)FormatException
            • .NET 6:拋出 FormatException
            • 這是正確的行為,但在這里調(diào)用它是為了與下一個(gè)示例進(jìn)行對(duì)比。
          • 32.ToString("H100")->H132
            • .NET 6:拋出FormatException
            • .NET 5:H是無(wú)效的格式說明符。所以,我們應(yīng)該拋出一個(gè)FormatException. 相反,我們將精度 >= 100 解釋為自定義格式的錯(cuò)誤行為意味著我們返回了錯(cuò)誤的值。
          • double.Parse("9007199254740997.0")->9007199254740998
            • .NET 6 9007199254740996:。
            • .NET 5:9007199254740997.0不能完全以 IEEE 754 格式表示。使用我們當(dāng)前的舍入方案,正確的返回值應(yīng)該是9007199254740996. 但是,輸入的最后一部分迫使解析器錯(cuò)誤地舍入結(jié)果并返回。.09007199254740998

          System.Text.Json

          System.Text.Json提供多種高性能API 用于處理JSON 文檔。在過去的幾個(gè)版本中,我們添加了新功能,以進(jìn)一步提高JSON 處理性能并減輕對(duì)希望從NewtonSoft.Json遷移的人的阻礙。 此版本包括在該路徑上的繼續(xù),并且在性能方面向前邁出了一大步,特別是在序列化程序源生成器方面。

          JsonSerializer 源生成

          注意:使用.NET 6 RC1 或更早版本的源代碼生成的應(yīng)用程序應(yīng)重新編譯。

          幾乎所有.NET 序列化程序的支柱都是反射。反射對(duì)于某些場(chǎng)景來(lái)說是一種很好的能力,但不能作為高性能云原生應(yīng)用程序(通常(反)序列化和處理大量JSON 文檔)的基礎(chǔ)。反射是啟動(dòng)、內(nèi)存使用和程序集修整的問題。

          運(yùn)行時(shí)反射的替代方法是編譯時(shí)源代碼生成。在.NET 6 中,我們包含一個(gè)新的源代碼生成器作為 System.Text.Json. JSON 源代碼生成器可以與多種方式結(jié)合使用JsonSerializer并且可以通過多種方式進(jìn)行配置。

          它可以提供以下好處:

          • 減少啟動(dòng)時(shí)間
          • 提高序列化吞吐量
          • 減少私有內(nèi)存使用
          • 刪除運(yùn)行時(shí)使用System.ReflectionSystem.Reflection.Emit
          • IL 修整兼容性

          默認(rèn)情況下,JSON 源生成器為給定的可序列化類型發(fā)出序列化邏輯。JsonSerializer通過生成直接使用的源代碼,這提供了比使用現(xiàn)有方法更高的性能Utf8JsonWriter。簡(jiǎn)而言之,源代碼生成器提供了一種在編譯時(shí)為您提供不同實(shí)現(xiàn)的方法,以使運(yùn)行時(shí)體驗(yàn)更好。

          給定一個(gè)簡(jiǎn)單的類型:

          namespace Test
          {
              internal class JsonMessage
              {
                  public string Message { get; set; }
              }
          }

          源生成器可以配置為為示例JsonMessage類型的實(shí)例生成序列化邏輯。請(qǐng)注意,類名JsonContext是任意的。您可以為生成的源使用所需的任何類名。

          using System.Text.Json.Serialization;
          
          namespace Test
          {
              [JsonSerializable(typeof(JsonMessage)]
              internal partial class JsonContext : JsonSerializerContext
              {
              }
          }

          使用此模式的序列化程序調(diào)用可能類似于以下示例。此示例提供了可能的最佳性能。

          using MemoryStream ms = new();
          using Utf8JsonWriter writer = new(ms);
          
          JsonSerializer.Serialize(jsonMessage, JsonContext.Default.JsonMessage);
          writer.Flush();
          
          // Writer contains:
          // {"Message":"Hello, world!"}

          最快和最優(yōu)化的源代碼生成模式——基于Utf8JsonWriter——目前僅可用于序列化。Utf8JsonReader根據(jù)您的反饋,將來(lái)可能會(huì)提供對(duì)反序列化的類似支持。

          源生成器還發(fā)出類型元數(shù)據(jù)初始化邏輯,這也有利于反序列化。JsonMessage要反序列化使用預(yù)生成類型元數(shù)據(jù)的實(shí)例,您可以執(zhí)行以下操作:

          JsonSerializer.Deserialize(json, JsonContext.Default.JsonMessage);

          JsonSerializer 支持 IAsyncEnumerable

          您現(xiàn)在可以使用System.Text.Json(反)序列化IAsyncEnumerableJSON 數(shù)組。以下示例使用流作為任何異步數(shù)據(jù)源的表示。源可以是本地計(jì)算機(jī)上的文件,也可以是數(shù)據(jù)庫(kù)查詢或Web 服務(wù)API 調(diào)用的結(jié)果。

          JsonSerializer.SerializeAsync已更新以識(shí)別并為IAsyncEnumerable值提供特殊處理。

          using System;
          using System.Collections.Generic;
          using System.IO;
          using System.Text.Json;
          
          static async IAsyncEnumerable<int> PrintNumbers(int n)
          {
              for (int i = 0; i < n; i++) yield return i;
          }
          
          using Stream stream = Console.OpenStandardOutput();
          var data = new { Data = PrintNumbers(3) };
          await JsonSerializer.SerializeAsync(stream, data); // prints {"Data":[0,1,2]}

          IAsyncEnumerable僅使用異步序列化方法支持值。嘗試使用同步方法進(jìn)行序列化將導(dǎo)致NotSupportedException被拋出。

          流式反序列化需要一個(gè)新的 API 來(lái)返回IAsyncEnumerable<T>。我們?yōu)榇颂砑恿?/span>JsonSerializer.DeserializeAsyncEnumerable方法,您可以在以下示例中看到。

          using System;
          using System.IO;
          using System.Text;
          using System.Text.Json;
          
          var stream = new MemoryStream(Encoding.UTF8.GetBytes("[0,1,2,3,4]"));
          await foreach (int item in JsonSerializer.DeserializeAsyncEnumerable<int>(stream))
          {
              Console.WriteLine(item);
          }

          此示例將按需反序列化元素,并且在使用特別大的數(shù)據(jù)流時(shí)非常有用。它僅支持從根級(jí)JSON 數(shù)組讀取,盡管將來(lái)可能會(huì)根據(jù)反饋放寬。

          現(xiàn)有DeserializeAsync方法名義上支持IAsyncEnumerable<T>,但在其非流方法簽名的范圍內(nèi)。它必須將最終結(jié)果作為單個(gè)值返回,如以下示例所示。

          using System;
          using System.Collections.Generic;
          using System.IO;
          using System.Text;
          using System.Text.Json;
          
          var stream = new MemoryStream(Encoding.UTF8.GetBytes(@"{""Data"":[0,1,2,3,4]}"));
          var result = await JsonSerializer.DeserializeAsync<MyPoco>(stream);
          await foreach (int item in result.Data)
          {
              Console.WriteLine(item);
          }
          
          public class MyPoco
          {
              public IAsyncEnumerable<int> Data { get; set; }
          }

          在此示例中,反序列化器將IAsyncEnumerable在返回反序列化對(duì)象之前緩沖內(nèi)存中的所有內(nèi)容。這是因?yàn)榉葱蛄谢餍枰诜祷亟Y(jié)果之前消耗整個(gè) JSON 值。

          System.Text.Json:可寫 DOM 功能

          可寫JSON DOM 特性為System.Text.Json添加了一個(gè)新的簡(jiǎn)單且高性能的編程模型。這個(gè)新的API 很有吸引力,因?yàn)樗苊饬诵枰獜?qiáng)類型的序列化合約,并且與現(xiàn)有的JsonDocument類型相比,DOM 是可變的。

          這個(gè)新的 API 有以下好處:

          • 在使用POCO類型是不可能或不希望的情況下,或者當(dāng)JSON 模式不固定且必須檢查的情況下,序列化的輕量級(jí)替代方案。
          • 啟用對(duì)大樹子集的有效修改。例如,可以有效地導(dǎo)航到大型JSON 樹的子部分并從該子部分讀取數(shù)組或反序列化POCO。LINQ 也可以與它一起使用。

          以下示例演示了新的編程模型。

              // Parse a JSON object
              JsonNode jNode = JsonNode.Parse("{"MyProperty":42}");
              int value = (int)jNode["MyProperty"];
              Debug.Assert(value == 42);
              // or
              value = jNode["MyProperty"].GetValue<int>();
              Debug.Assert(value == 42);
          
              // Parse a JSON array
              jNode = JsonNode.Parse("[10,11,12]");
              value = (int)jNode[1];
              Debug.Assert(value == 11);
              // or
              value = jNode[1].GetValue<int>();
              Debug.Assert(value == 11);
          
              // Create a new JsonObject using object initializers and array params
              var jObject = new JsonObject
              {
                  ["MyChildObject"] = new JsonObject
                  {
                      ["MyProperty"] = "Hello",
                      ["MyArray"] = new JsonArray(10, 11, 12)
                  }
              };
          
              // Obtain the JSON from the new JsonObject
              string json = jObject.ToJsonString();
              Console.WriteLine(json); // {"MyChildObject":{"MyProperty":"Hello","MyArray":[10,11,12]}}
          
              // Indexers for property names and array elements are supported and can be chained
          Debug.Assert(jObject["MyChildObject"]["MyArray"][1].GetValue<int>() == 11);

          ReferenceHandler.IgnoreCycles

          JsonSerializer(System.Text.Json)現(xiàn)在支持在序列化對(duì)象圖時(shí)忽略循環(huán)的能力。該ReferenceHandler.IgnoreCycles選項(xiàng)具有與Newtonsoft.Json ReferenceLoopHandling.Ignore類似的行為。一個(gè)關(guān)鍵區(qū)別是System.Text.Json 實(shí)現(xiàn)用null JSON 標(biāo)記替換引用循環(huán),而不是忽略對(duì)象引用。

          您可以在以下示例中看到ReferenceHandler.IgnoreCycles的行為。在這種情況下,該Next屬性被序列化為null,因?yàn)榉駝t它會(huì)創(chuàng)建一個(gè)循環(huán)。

          class Node
          {
              public string Description { get; set; }
              public object Next { get; set; }
          }
          
          void Test()
          {
              var node = new Node { Description = "Node 1" };
              node.Next = node;
          
              var opts = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles };
          
              string json = JsonSerializer.Serialize(node, opts);
              Console.WriteLine(json); // Prints {"Description":"Node 1","Next":null}
          }

          源代碼構(gòu)建

          通過源代碼構(gòu)建,您只需幾個(gè)命令即可在您自己的計(jì)算機(jī)上從源代碼構(gòu)建.NET SDK 。讓我解釋一下為什么這個(gè)項(xiàng)目很重要。

          源代碼構(gòu)建是一個(gè)場(chǎng)景,也是我們?cè)诎l(fā)布.NET Core 1.0 之前一直與Red Hat 合作開發(fā)的基礎(chǔ)架構(gòu)。幾年后,我們非常接近于交付它的全自動(dòng)版本。對(duì)于Red Hat Enterprise Linux (RHEL) .NET 用戶來(lái)說,這個(gè)功能很重要。Red Hat 告訴我們,.NET 已經(jīng)發(fā)展成為其生態(tài)系統(tǒng)的重要開發(fā)者平臺(tái)。好的!

          Linux 發(fā)行版的黃金標(biāo)準(zhǔn)是使用作為發(fā)行版存檔一部分的編譯器和工具鏈構(gòu)建開源代碼。這適用于.NET 運(yùn)行時(shí)(用C++ 編寫),但不適用于任何用C# 編寫的代碼。對(duì)于C# 代碼,我們使用兩遍構(gòu)建機(jī)制來(lái)滿足發(fā)行版要求。這有點(diǎn)復(fù)雜,但了解流程很重要。

          Red Hat 使用.NET SDK (#1) 的Microsoft 二進(jìn)制構(gòu)建來(lái)構(gòu)建.NET SDK 源代碼,以生成SDK (#2) 的純開源二進(jìn)制構(gòu)建。之后,使用這個(gè)新版本的SDK (#2) 再次構(gòu)建相同的SDK 源代碼,以生成可證明的開源SDK (#3)。.NET SDK (#3) 的最終二進(jìn)制版本隨后可供RHEL 用戶使用。之后,Red Hat 可以使用相同的SDK (#3) 來(lái)構(gòu)建新的.NET 版本,而不再需要使用Microsoft SDK 來(lái)構(gòu)建每月更新。

          這個(gè)過程可能令人驚訝和困惑。開源發(fā)行版需要通過開源工具構(gòu)建。此模式確保不需要Microsoft 構(gòu)建的SDK,無(wú)論是有意還是無(wú)意。作為開發(fā)者平臺(tái),包含在發(fā)行版中的門檻比僅使用兼容許可證的門檻更高。源代碼構(gòu)建項(xiàng)目使.NET 能夠滿足該標(biāo)準(zhǔn)。

          源代碼構(gòu)建的可交付成果是源代碼壓縮包。源tarball 包含SDK 的所有源(對(duì)于給定版本)。從那里,紅帽(或其他組織)可以構(gòu)建自己的SDK 版本。Red Hat 政策要求使用內(nèi)置源工具鏈來(lái)生成二進(jìn)制tar 球,這就是他們使用兩遍方法的原因。但是源代碼構(gòu)建本身不需要這種兩遍方法。

          在Linux 生態(tài)系統(tǒng)中,給定組件同時(shí)擁有源和二進(jìn)制包或tarball 是很常見的。我們已經(jīng)有了可用的二進(jìn)制tarball,現(xiàn)在也有了源tarball。這使得.NET 與標(biāo)準(zhǔn)組件模式相匹配。

          .NET 6 的重大改進(jìn)是源tarball 現(xiàn)在是我們構(gòu)建的產(chǎn)品。它過去需要大量的人工來(lái)制作,這也導(dǎo)致將源tarball 交付給Red Hat 的延遲很長(zhǎng)。雙方都對(duì)此不滿意。

          在這個(gè)項(xiàng)目上,我們與紅帽密切合作五年多。它的成功在很大程度上要?dú)w功于我們有幸與之共事的優(yōu)秀紅帽工程師的努力。其他發(fā)行版和組織已經(jīng)并將從他們的努力中受益。

          附帶說明一下,源代碼構(gòu)建是朝著可重現(xiàn)構(gòu)建邁出的一大步,我們也堅(jiān)信這一點(diǎn)。.NET SDK 和C# 編譯器具有重要的可重現(xiàn)構(gòu)建功能。

          庫(kù) API

          除了已經(jīng)涵蓋的API 之外,還添加了以下API。

          WebSocket 壓縮

          壓縮對(duì)于通過網(wǎng)絡(luò)傳輸?shù)娜魏螖?shù)據(jù)都很重要。WebSockets 現(xiàn)在啟用壓縮。我們使用了WebSockets 的擴(kuò)展permessage-deflate實(shí)現(xiàn),RFC 7692。它允許使用該DEFLATE算法壓縮WebSockets 消息負(fù)載。此功能是GitHub 上Networking 的主要用戶請(qǐng)求之一。

          與加密一起使用的壓縮可能會(huì)導(dǎo)致攻擊,例如CRIME和BREACH。這意味著不能在單個(gè)壓縮上下文中將秘密與用戶生成的數(shù)據(jù)一起發(fā)送,否則可以提取該秘密。為了讓用戶注意到這些影響并幫助他們權(quán)衡風(fēng)險(xiǎn),我們將其中一個(gè)關(guān)鍵API 命名為DangerousDeflateOptions。我們還添加了關(guān)閉特定消息壓縮的功能,因此如果用戶想要發(fā)送秘密,他們可以在不壓縮的情況下安全地執(zhí)行此操作。

          禁用壓縮時(shí)WebSocket的內(nèi)存占用減少了約27%。

          從客戶端啟用壓縮很容易,如下例所示。但是,請(qǐng)記住,服務(wù)器可以協(xié)商設(shè)置,例如請(qǐng)求更小的窗口或完全拒絕壓縮。

          var cws = new ClientWebSocket();
          cws.Options.DangerousDeflateOptions = new WebSocketDeflateOptions()
          {
              ClientMaxWindowBits = 10,
              ServerMaxWindowBits = 10
          };

          還添加了對(duì) ASP.NET Core 的 WebSocket 壓縮支持。

          歸功于伊萬(wàn)茲拉塔諾夫。

          Socks 代理支持

          SOCKS是一種代理服務(wù)器實(shí)現(xiàn),可以處理任何TCP 或UDP 流量,使其成為一個(gè)非常通用的系統(tǒng)。這是一個(gè)長(zhǎng)期存在的社區(qū)請(qǐng)求,已添加到.NET 6中。

          此更改增加了對(duì)Socks4、Socks4a 和Socks5 的支持。例如,它可以通過SSH 測(cè)試外部連接或連接到 Tor 網(wǎng)絡(luò)。

          該類WebProxy現(xiàn)在接受socks方案,如以下示例所示。

          var handler = new HttpClientHandler
          {
              Proxy = new WebProxy("socks5://127.0.0.1", 9050)
          };
          var httpClient = new HttpClient(handler);

          歸功于Huo yaoyuan。

          Microsoft.Extensions.Hosting — 配置主機(jī)選項(xiàng) API

          我們?cè)贗HostBuilder 上添加了一個(gè)新的ConfigureHostOptions API,以簡(jiǎn)化應(yīng)用程序設(shè)置(例如,配置關(guān)閉超時(shí)):

          using HostBuilder host = new()
              .ConfigureHostOptions(o =>
              {
                  o.ShutdownTimeout = TimeSpan.FromMinutes(10);
              })
              .Build();
          
          host.Run();

          在.NET 5 中,配置主機(jī)選項(xiàng)有點(diǎn)復(fù)雜:

          using HostBuilder host = new()
              .ConfigureServices(services =>
              {
                  services.Configure<HostOptions>(o =>
                  {
                      o.ShutdownTimeout = TimeSpan.FromMinutes(10);
                  });
              })
              .Build();
          
          host.Run();

          Microsoft.Extensions.DependencyInjection — CreateAsyncScope API

          CreateAsyncScope創(chuàng)建API是為了處理服務(wù)的處置IAsyncDisposable。以前,您可能已經(jīng)注意到處置IAsyncDisposable服務(wù)提供者可能會(huì)引發(fā)InvalidOperationException異常。

          以下示例演示了新模式,CreateAsyncScope用于啟用using語(yǔ)句的安全使用。

          await using (var scope = provider.CreateAsyncScope())
          {
              var foo = scope.ServiceProvider.GetRequiredService<Foo>();
          }

          以下示例演示了現(xiàn)有的問題案例:

          using System;
          using System.Threading.Tasks;
          using Microsoft.Extensions.DependencyInjection;
          
          await using var provider = new ServiceCollection()
                  .AddScoped<Foo>()
                  .BuildServiceProvider();
          
          // This using can throw InvalidOperationException
          using (var scope = provider.CreateScope())
          {
              var foo = scope.ServiceProvider.GetRequiredService<Foo>();
          }
          
          class Foo : IAsyncDisposable
          {
              public ValueTask DisposeAsync() => default;
          }

          以下模式是先前建議的避免異常的解決方法。不再需要它。

          var scope = provider.CreateScope();
          var foo = scope.ServiceProvider.GetRequiredService<Foo>();
          await ((IAsyncDisposable)scope).DisposeAsync();

          感謝Martin Bj?rkstr?m 。

          Microsoft.Extensions.Logging — 編譯時(shí)源生成器

          .NET 6 引入了LoggerMessageAttribute類型。 此屬性是Microsoft.Extensions.Logging命名空間的一部分,使用時(shí),它會(huì)源生成高性能日志記錄API。源生成日志支持旨在為現(xiàn)代.NET 應(yīng)用程序提供高度可用和高性能的日志解決方案。自動(dòng)生成的源代碼依賴于ILogger接口和LoggerMessage.Define功能。

          LoggerMessageAttribute源生成器在用于partial日志記錄方法時(shí)觸發(fā)。當(dāng)被觸發(fā)時(shí),它要么能夠自動(dòng)生成partial它正在裝飾的方法的實(shí)現(xiàn),要么生成編譯時(shí)診斷,并提供有關(guān)正確使用的提示。編譯時(shí)日志記錄解決方案在運(yùn)行時(shí)通常比現(xiàn)有的日志記錄方法快得多。它通過最大限度地消除裝箱、臨時(shí)分配和副本來(lái)實(shí)現(xiàn)這一點(diǎn)。

          與直接手動(dòng)使用LoggerMessage.Define API相比,有以下好處:

          • 更短更簡(jiǎn)單的語(yǔ)法:聲明性屬性使用而不是編碼樣板。
          • 引導(dǎo)式開發(fā)人員體驗(yàn):生成器發(fā)出警告以幫助開發(fā)人員做正確的事情。
          • 支持任意數(shù)量的日志記錄參數(shù)。LoggerMessage.Define最多支持六個(gè)。
          • 支持動(dòng)態(tài)日志級(jí)別。這是LoggerMessage.Define單獨(dú)不可能的。

          要使用LoggerMessageAttribute,消費(fèi)類和方法需要是partial。代碼生成器在編譯時(shí)觸發(fā)并生成partial方法的實(shí)現(xiàn)。

          public static partial class Log
          {
              [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Could not open socket to `{hostName}`")]
              public static partial void CouldNotOpenSocket(ILogger logger, string hostName);
          }

          在前面的示例中,日志記錄方法是static,并且在屬性定義中指定了日志級(jí)別。在靜態(tài)上下文中使用屬性時(shí),ILogger需要實(shí)例作為參數(shù)。您也可以選擇在非靜態(tài)上下文中使用該屬性。有關(guān)更多示例和使用場(chǎng)景,請(qǐng)?jiān)L問編譯時(shí)日志記錄源生成器文檔。

          System.Linq — 可枚舉的支持 Index 和 Range 參數(shù)

          Enumerable.ElementAt方法現(xiàn)在接受來(lái)自可枚舉末尾的索引,如以下示例所示。

          Enumerable.Range(1, 10).ElementAt(^2); // returns 9

          添加了一個(gè)Enumerable.Take接受Range參數(shù)的重載。它簡(jiǎn)化了對(duì)可枚舉序列的切片:

          • source.Take(..3)代替source.Take(3)
          • source.Take(3..)代替source.Skip(3)
          • source.Take(2..7)代替source.Take(7).Skip(2)
          • source.Take(^3..)代替source.TakeLast(3)
          • source.Take(..^3)代替source.SkipLast(3)
          • source.Take(7..3)而不是.source.TakeLast(7).SkipLast(3)

          感謝@dixin 。

          System.Linq — TryGetNonEnumeratedCount

          TryGetNonEnumeratedCount方法嘗試在不強(qiáng)制枚舉的情況下獲取源可枚舉的計(jì)數(shù)。這種方法在枚舉之前預(yù)分配緩沖區(qū)很有用的場(chǎng)景中很有用,如下面的示例所示。

          List<T> buffer = source.TryGetNonEnumeratedCount(out int count) ? new List<T>(capacity: count) : new List<T>();
          foreach (T item in source)
          {
              buffer.Add(item);
          }

          TryGetNonEnumeratedCount檢查實(shí)現(xiàn)ICollection/ ICollection<T>;或利用Linq 采用的一些內(nèi)部?jī)?yōu)化的源。

          System.Linq — DistinctBy / UnionBy / IntersectBy / ExceptBy

          新變體已添加到允許使用鍵選擇器函數(shù)指定相等性的集合操作中,如下例所示。

          Enumerable.Range(1, 20).DistinctBy(x => x % 3); // {1, 2, 3}
          
          var first = new (string Name, int Age)[] { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40) };
          var second = new (string Name, int Age)[] { ("Claire", 30), ("Pat", 30), ("Drew", 33) };
          first.UnionBy(second, person => person.Age); // { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40), ("Drew", 33) }

          System.Linq - MaxBy / MinBy

          MaxByMinBy方法允許使用鍵選擇器查找最大或最小元素,如下例所示。

          var people = new (string Name, int Age)[] { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40) };
          people.MaxBy(person => person.Age); // ("Ashley", 40)

          System.Linq — Chunk

          Chunk可用于將可枚舉的源分塊為固定大小的切片,如下例所示。

          IEnumerable<int[]> chunks = Enumerable.Range(0, 10).Chunk(size: 3); // { {0,1,2}, {3,4,5}, {6,7,8}, {9} }

          歸功于羅伯特安德森。

          System.Linq—— // FirstOrDefault 采用默認(rèn)參數(shù)的重載 LastOrDefaultSingleOrDefault

          如果源可枚舉為空,則現(xiàn)有的FirstOrDefault /LastOrDefault /SingleOrDefault方法返回default(T)。添加了新的重載,它們接受在這種情況下返回的默認(rèn)參數(shù),如以下示例所示。

          Enumerable.Empty\<int\>().SingleOrDefault(-1); // returns -1

          感謝@ Foxtrek64 。

          System.Linq — Zip 接受三個(gè)可枚舉的重載

          Zip方法現(xiàn)在支持組合三個(gè)枚舉,如以下示例所示。

          var xs = Enumerable.Range(1, 10);
          var ys = xs.Select(x => x.ToString());
          var zs = xs.Select(x => x % 2 == 0);
          
          foreach ((int x, string y, bool z) in Enumerable.Zip(xs,ys,zs))
          {
          }

          歸功于Huo yaoyuan。

          優(yōu)先隊(duì)列

          PriorityQueue<TElement, TPriority>(System.Collections.Generic) 是一個(gè)新集合,可以添加具有值和優(yōu)先級(jí)的新項(xiàng)目。在出隊(duì)時(shí),PriorityQueue 返回具有最低優(yōu)先級(jí)值的元素。您可以認(rèn)為這個(gè)新集合類似于Queue<T>但每個(gè)入隊(duì)元素都有一個(gè)影響出隊(duì)行為的優(yōu)先級(jí)值。

          以下示例演示了.PriorityQueue<string, int>

           // creates a priority queue of strings with integer priorities
          var pq = new PriorityQueue<string, int>();
          
          // enqueue elements with associated priorities
          pq.Enqueue("A", 3);
          pq.Enqueue("B", 1);
          pq.Enqueue("C", 2);
          pq.Enqueue("D", 3);
          
          pq.Dequeue(); // returns "B"
          pq.Dequeue(); // returns "C"
          pq.Dequeue(); // either "A" or "D", stability is not guaranteed.

          歸功于Patryk Golebiowski。

          更快地將結(jié)構(gòu)處理為字典值

          CollectionsMarshal.GetValueRef是一個(gè)新的 不安全 API,它可以更快地更新字典中的結(jié)構(gòu)值。新API 旨在用于高性能場(chǎng)景,而不是用于一般用途。它返回ref結(jié)構(gòu)值,然后可以使用典型技術(shù)對(duì)其進(jìn)行更新。

          以下示例演示了如何使用新API:

          ref MyStruct value = CollectionsMarshal.GetValueRef(dictionary, key);
          // Returns Unsafe.NullRef<TValue>() if it doesn't exist; check using Unsafe.IsNullRef(ref value)
          if (!Unsafe.IsNullRef(ref value))
          {
              // Mutate in-place
              value.MyInt++;
          }

          在此更改之前,更新struct字典值對(duì)于高性能場(chǎng)景可能會(huì)很昂貴,需要字典查找和復(fù)制到堆棧的struct. 然后在更改之后struct,它將再次分配給字典鍵,從而導(dǎo)致另一個(gè)查找和復(fù)制操作。這種改進(jìn)將密鑰散列減少到1(從2)并刪除了所有結(jié)構(gòu)復(fù)制操作。

          歸功于本亞當(dāng)斯。

          新建 DateOnly 和 TimeOnly 結(jié)構(gòu)

          添加了僅限日期和時(shí)間的結(jié)構(gòu),具有以下特征:

          • 每個(gè)都代表a 的一半DateTime,或者只是日期部分,或者只是時(shí)間部分。
          • DateOnly非常適合生日、周年紀(jì)念日和工作日。它與SQL Server 的date類型一致。
          • TimeOnly非常適合定期會(huì)議、鬧鐘和每周工作時(shí)間。它與SQL Server 的time類型一致。
          • 補(bǔ)充現(xiàn)有的日期/時(shí)間類型( DateTime, DateTimeOffset, TimeSpan, TimeZoneInfo)。
          • System命名空間中,在CoreLib 中提供,就像現(xiàn)有的相關(guān)類型一樣。

          性能改進(jìn) DateTime.UtcNow

          這種改進(jìn)具有以下好處:

          • 修復(fù)了在Windows 上獲取系統(tǒng)時(shí)間的2.5 倍性能回歸。
          • 利用Windows 閏秒數(shù)據(jù)的5 分鐘滑動(dòng)緩存,而不是在每次調(diào)用時(shí)獲取。

          在所有平臺(tái)上支持 Windows 和 IANA 時(shí)區(qū)

          這種改進(jìn)具有以下好處:

          • 使用時(shí)的隱式轉(zhuǎn)換(https://github.com/dotnet/runtime/pull/49412)TimeZoneInfo.FindSystemTimeZoneById
          • TimeZoneInfo通過: TryConvertIanaIdToWindowsIdTryConvertWindowsIdToIanaIdHasIanaId(https://github.com/dotnet/runtime/issues/49407)上的新API 進(jìn)行顯式轉(zhuǎn)換
          • 改進(jìn)了使用不同時(shí)區(qū)類型的系統(tǒng)之間的跨平臺(tái)支持和互操作。
          • 刪除需要使用TimeZoneConverter OSS 庫(kù)。該功能現(xiàn)在是內(nèi)置的。

          改進(jìn)的時(shí)區(qū)顯示名稱

          Unix 上的時(shí)區(qū)顯示名稱已得到改進(jìn):

          • 消除由.返回的列表中的顯示名稱的歧義。TimeZoneInfo.GetSystemTimeZones
          • 利用ICU / CLDR 全球化數(shù)據(jù)。
          • 僅適用于Unix。Windows 仍然使用注冊(cè)表數(shù)據(jù)。這可能會(huì)在以后更改。

          還進(jìn)行了以下附加改進(jìn):

          • UTC 時(shí)區(qū)的顯示名稱和標(biāo)準(zhǔn)名稱被硬編碼為英語(yǔ),現(xiàn)在使用與其余時(shí)區(qū)數(shù)據(jù)相同的語(yǔ)言(CurrentUICulture在Unix 上,Windows 上的操作系統(tǒng)默認(rèn)語(yǔ)言)。
          • 由于大小限制,Wasm 中的時(shí)區(qū)顯示名稱改為使用非本地化IANA ID。
          • TimeZoneInfo.AdjustmentRule嵌套類將其BaseUtcOffsetDelta內(nèi)部屬性公開,并獲得一個(gè)新的構(gòu)造函數(shù),該構(gòu)造函數(shù)baseUtcOffsetDelta作為參數(shù)。(https://github.com/dotnet/runtime/issues/50256)
          • TimeZoneInfo.AdjustmentRule還獲得了在Unix 上加載時(shí)區(qū)的各種修復(fù)(https://github.com/dotnet/runtime/pull/49733), (https://github.com/dotnet/runtime/pull/50131)

          改進(jìn)了對(duì) Windows ACL 的支持

          System.Threading.AccessControl現(xiàn)在包括對(duì)與Windows 訪問控制列表(ACL) 交互的改進(jìn)支持。新的重載被添加到MutexSemaphoreOpenExistingTryOpenExisting方法EventWaitHandle中。這些具有“安全權(quán)限”實(shí)例的重載允許打開使用特殊Windows 安全屬性創(chuàng)建的線程同步對(duì)象的現(xiàn)有實(shí)例。

          此更新與.NET Framework 中可用的API 匹配并且具有相同的行為。

          以下示例演示了如何使用這些新API。

          對(duì)于Mutex

          var rights = MutexRights.FullControl;
          string mutexName = "MyMutexName";
          
          var security = new MutexSecurity();
          SecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
          MutexAccessRule accessRule = new MutexAccessRule(identity, rights, AccessControlType.Allow);
          security.AddAccessRule(accessRule);
          
          // createdMutex, openedMutex1 and openedMutex2 point to the same mutex
          Mutex createdMutex = MutexAcl.Create(initiallyOwned: true, mutexName, out bool createdNew, security);
          Mutex openedMutex1 = MutexAcl.OpenExisting(mutexName, rights);
          MutexAcl.TryOpenExisting(mutexName, rights, out Mutex openedMutex2);

          為了Semaphore

           var rights = SemaphoreRights.FullControl;
          string semaphoreName = "MySemaphoreName";
          
          var security = new SemaphoreSecurity();
          SecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
          SemaphoreAccessRule accessRule = new SemaphoreAccessRule(identity, rights, AccessControlType.Allow);
          security.AddAccessRule(accessRule);
          
          // createdSemaphore, openedSemaphore1 and openedSemaphore2 point to the same semaphore
          Semaphore createdSemaphore = SemaphoreAcl.Create(initialCount: 1,  maximumCount: 3, semaphoreName, out bool createdNew, security);
          Semaphore openedSemaphore1 = SemaphoreAcl.OpenExisting(semaphoreName, rights);
          SemaphoreAcl.TryOpenExisting(semaphoreName, rights, out Semaphore openedSemaphore2);

          為了EventWaitHandle

           var rights = EventWaitHandleRights.FullControl;
          string eventWaitHandleName = "MyEventWaitHandleName";
          
          var security = new EventWaitHandleSecurity();
          SecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
          EventWaitHandleAccessRule accessRule = new EventWaitHandleAccessRule(identity, rights, AccessControlType.Allow);
          security.AddAccessRule(accessRule);
          
          // createdHandle, openedHandle1 and openedHandle2 point to the same event wait handle
          EventWaitHandle createdHandle = EventWaitHandleAcl.Create(initialState: true, EventResetMode.AutoReset, eventWaitHandleName, out bool createdNew, security);
          EventWaitHandle openedHandle1 = EventWaitHandleAcl.OpenExisting(eventWaitHandleName, rights);
          EventWaitHandleAcl.TryOpenExisting(eventWaitHandleName, rights, out EventWaitHandle openedHandle2);

          HMAC 一次性方法

          System.Security.CryptographyHMAC類現(xiàn)在具有允許一次性計(jì)算HMAC而無(wú)需分配的靜態(tài)方法。這些添加類似于在先前版本中添加的用于哈希生成的一次性方法。

          DependentHandle 現(xiàn)已公開

          DependentHandle類型現(xiàn)在是公共的,具有以下 API 表面:

          namespace System.Runtime
          {
              public struct DependentHandle : IDisposable
              {
                  public DependentHandle(object? target, object? dependent);
                  public bool IsAllocated { get; }
                  public object? Target { get; set; }
                  public object? Dependent { get; set; }
                  public (object? Target, object? Dependent) TargetAndDependent { get; }
                  public void Dispose();
              }
          }

          它可用于創(chuàng)建高級(jí)系統(tǒng),例如復(fù)雜的緩存系統(tǒng)或ConditionalWeakTable<TKey, TValue>類型?的自定義版本。例如,它將被MVVM Toolkit中的WeakReferenceMessenger類型使用,以避免在廣播消息時(shí)分配內(nèi)存。

          可移植線程池

          .NET 線程池已作為托管實(shí)現(xiàn)重新實(shí)現(xiàn),現(xiàn)在用作.NET 6 中的默認(rèn)線程池。我們進(jìn)行此更改以使所有.NET 應(yīng)用程序都可以訪問同一個(gè)線程池,而不管是否正在使用CoreCLR、Mono 或任何其他運(yùn)行時(shí)。作為此更改的一部分,我們沒有觀察到或預(yù)期任何功能或性能影響。

          RyuJIT

          該團(tuán)隊(duì)在此版本中對(duì).NET JIT 編譯器進(jìn)行了許多改進(jìn),在每個(gè)預(yù)覽帖子中都有記錄。這些更改中的大多數(shù)都提高了性能。這里介紹了一些RyuJIT 的亮點(diǎn)。

          動(dòng)態(tài) PGO

          在.NET 6 中,我們啟用了兩種形式的PGO(配置文件引導(dǎo)優(yōu)化):

          • 動(dòng)態(tài) PGO使用從當(dāng)前運(yùn)行中收集的數(shù)據(jù)來(lái)優(yōu)化當(dāng)前運(yùn)行。
          • 靜態(tài)PGO依靠從過去運(yùn)行中收集的數(shù)據(jù)來(lái)優(yōu)化未來(lái)運(yùn)行。

          動(dòng)態(tài)PGO 已經(jīng)在文章前面的性能部分中介紹過。我將提供一個(gè)重新上限。

          動(dòng)態(tài)PGO 使JIT 能夠在運(yùn)行時(shí)收集有關(guān)實(shí)際用于特定應(yīng)用程序運(yùn)行的代碼路徑和類型的信息。然后,JIT 可以根據(jù)這些代碼路徑優(yōu)化代碼,有時(shí)會(huì)顯著提高性能。我們?cè)跍y(cè)試和生產(chǎn)中都看到了兩位數(shù)的健康改進(jìn)。有一組經(jīng)典的編譯器技術(shù)在沒有PGO 的情況下使用JIT 或提前編譯都無(wú)法實(shí)現(xiàn)。我們現(xiàn)在能夠應(yīng)用這些技術(shù)。熱/冷分離是一種這樣的技術(shù),而去虛擬化是另一種技術(shù)。

          要啟用動(dòng)態(tài)PGO,請(qǐng)?jiān)趹?yīng)用程序?qū)⑦\(yùn)行的環(huán)境中進(jìn)行設(shè)置DOTNET\_TieredPGO=1

          如性能部分所述,動(dòng)態(tài)PGO 將TechEmpower JSON"MVC"套件每秒的請(qǐng)求數(shù)提高了26%(510K -> 640K)。這是一個(gè)驚人的改進(jìn),無(wú)需更改代碼。

          我們的目標(biāo)是在未來(lái)的.NET 版本中默認(rèn)啟用動(dòng)態(tài)PGO,希望在.NET 7 中啟用。我們強(qiáng)烈建議您在應(yīng)用程序中嘗試動(dòng)態(tài)PGO 并向我們提供反饋。

          完整的 PGO

          要充分利用Dynamic PGO,您可以設(shè)置兩個(gè)額外的環(huán)境變量:DOTNET\_TC\_QuickJitForLoops=1DOTNET\_ReadyToRun=0。 這確保了盡可能多的方法參與分層編譯。我們將此變體稱為 Full PGO 。與動(dòng)態(tài)PGO 相比,完整PGO 可以提供更大的穩(wěn)態(tài)性能優(yōu)勢(shì),但啟動(dòng)時(shí)間會(huì)更慢(因?yàn)楸仨氃诘? 層運(yùn)行更多方法)。

          您不希望將此選項(xiàng)用于短期運(yùn)行的無(wú)服務(wù)器應(yīng)用程序,但對(duì)于長(zhǎng)期運(yùn)行的應(yīng)用程序可能有意義。

          在未來(lái)的版本中,我們計(jì)劃精簡(jiǎn)和簡(jiǎn)化這些選項(xiàng),以便您可以更簡(jiǎn)單地獲得完整PGO 的好處并用于更廣泛的應(yīng)用程序。

          靜態(tài) PGO

          我們目前使用 靜態(tài) PGO 來(lái)優(yōu)化.NET 庫(kù)程序集,例如R2R(Ready To Run)附帶的程序集System.Private.CoreLib

          靜態(tài)PGO 的好處是,在使用crossgen 將程序集編譯為R2R 格式時(shí)會(huì)進(jìn)行優(yōu)化。這意味著有運(yùn)行時(shí)的好處而沒有運(yùn)行時(shí)成本。這是非常重要的,也是PGO 對(duì)C++ 很重要的原因,例如。

          循環(huán)對(duì)齊

          內(nèi)存對(duì)齊是現(xiàn)代計(jì)算中各種操作的共同要求。在.NET 5 中,我們開始在 32 字節(jié)邊界對(duì)齊方法。在.NET 6 中,我們添加了一項(xiàng)執(zhí)行自適應(yīng)循環(huán)對(duì)齊的功能,該功能在具有循環(huán)的方法中添加NOP填充指令,以便循環(huán)代碼從mod(16) 或mod(32) 內(nèi)存地址開始。這些更改改進(jìn)并穩(wěn)定了.NET 代碼的性能。

          在下面的冒泡排序圖中,數(shù)據(jù)點(diǎn)1 表示我們開始在32 字節(jié)邊界對(duì)齊方法的點(diǎn)。數(shù)據(jù)點(diǎn)2 表示我們也開始對(duì)齊內(nèi)部循環(huán)的點(diǎn)。如您所見,基準(zhǔn)測(cè)試的性能和穩(wěn)定性都有很大提高。

          硬件加速結(jié)構(gòu)

          結(jié)構(gòu)是CLR 類型系統(tǒng)的重要組成部分。近年來(lái),它們經(jīng)常被用作整個(gè).NET 庫(kù)中的性能原語(yǔ)。最近的例子ValueTaskValueTupleSpan<T>。記錄結(jié)構(gòu)是一個(gè)新的例子。在.NET 5 和.NET 6 中,我們一直在提高結(jié)構(gòu)的性能,部分原因是通過確保結(jié)構(gòu)是局部變量、參數(shù)或方法的返回值時(shí)可以保存在超快速CPU 寄存器中)。這對(duì)于使用向量計(jì)算的API 特別有用。

          穩(wěn)定性能測(cè)量

          團(tuán)隊(duì)中有大量從未出現(xiàn)在博客上的工程系統(tǒng)工作。這對(duì)于您使用的任何硬件或軟件產(chǎn)品都是如此。JIT 團(tuán)隊(duì)開展了一個(gè)項(xiàng)目來(lái)穩(wěn)定性能測(cè)量,目標(biāo)是增加我們內(nèi)部性能實(shí)驗(yàn)室自動(dòng)化自動(dòng)報(bào)告的回歸值。這個(gè)項(xiàng)目很有趣,因?yàn)樾枰M(jìn)行深入調(diào)查和產(chǎn)品更改才能實(shí)現(xiàn)穩(wěn)定性。它還展示了我們?yōu)楸3趾吞岣呖?jī)效而衡量的規(guī)模。

          此圖像演示了不穩(wěn)定的性能測(cè)量,其中性能在連續(xù)運(yùn)行中在慢速和快速之間波動(dòng)。x 軸是測(cè)試日期,y 軸是測(cè)試時(shí)間,以納秒為單位。到圖表末尾(提交這些更改后),您可以看到測(cè)量值穩(wěn)定,結(jié)果最好。這張圖片展示了一個(gè)單一的測(cè)試。還有更多測(cè)試在dotnet/runtime #43227中被證明具有類似的行為。

          即用型代碼 /Crossgen 2

          Crossgen2 是crossgen 工具的替代品。它旨在滿足兩個(gè)結(jié)果:

          • 讓crossgen開發(fā)更高效。
          • 啟用一組目前無(wú)法通過crossgen 實(shí)現(xiàn)的功能。

          這種轉(zhuǎn)換有點(diǎn)類似于本機(jī)代碼csc.exe 到托管代碼Roslyn 編譯器。Crossgen2 是用C# 編寫的,但是它沒有像Roslyn 那樣公開一個(gè)花哨的API。

          我們可能已經(jīng)/已經(jīng)為.NET 6 和7 計(jì)劃了六個(gè)項(xiàng)目,這些項(xiàng)目依賴于crossgen2。矢量指令默認(rèn)提議是我們希望為.NET 6 但更可能是.NET 7 進(jìn)行的crossgen2 功能和產(chǎn)品更改的一個(gè)很好的例子。版本氣泡是另一個(gè)很好的例子。

          Crossgen2 支持跨操作系統(tǒng)和架構(gòu)維度的交叉編譯(因此稱為"crossgen")。這意味著您將能夠使用單個(gè)構(gòu)建機(jī)器為所有目標(biāo)生成本機(jī)代碼,至少與準(zhǔn)備運(yùn)行的代碼相關(guān)。但是,運(yùn)行和測(cè)試該代碼是另一回事,為此您需要合適的硬件和操作系統(tǒng)。

          第一步是用crossgen2編譯平臺(tái)本身。我們使用.NET 6 完成了所有架構(gòu)的任務(wù)。因此,我們能夠在此版本中淘汰舊的crossgen。請(qǐng)注意,crossgen2 僅適用于CoreCLR,而不適用于基于Mono 的應(yīng)用程序(它們具有一組單獨(dú)的代碼生成工具)。

          這個(gè)項(xiàng)目——至少一開始——并不以性能為導(dǎo)向。目標(biāo)是啟用更好的架構(gòu)來(lái)托管RyuJIT(或任何其他)編譯器以離線方式生成代碼(不需要或啟動(dòng)運(yùn)行時(shí))。

          你可能會(huì)說“嘿……如果是用C# 編寫的,難道你不需要啟動(dòng)運(yùn)行時(shí)來(lái)運(yùn)行crossgen2 嗎?” 是的,但這不是本文中“離線”的含義。當(dāng)crossgen2 運(yùn)行時(shí),我們不使用運(yùn)行crossgen2 的運(yùn)行時(shí)附帶的JIT 來(lái)生成準(zhǔn)備運(yùn)行(R2R) 代碼. 那是行不通的,至少對(duì)于我們的目標(biāo)來(lái)說是行不通的。想象一下crossgen2 在x64 機(jī)器上運(yùn)行,我們需要為Arm64 生成代碼。Crossgen2 將Arm64 RyuJIT(針對(duì)x64 編譯)加載為原生插件,然后使用它生成Arm64 R2R 代碼。機(jī)器指令只是保存到文件中的字節(jié)流。它也可以在相反的方向工作。在Arm64 上,crossgen2 可以使用編譯為Arm64 的x64 RyuJIT 生成x64 代碼。我們使用相同的方法來(lái)針對(duì)x64 機(jī)器上的x64 代碼。Crossgen2 會(huì)加載一個(gè)RyuJIT,它是為任何需要的配置而構(gòu)建的。這可能看起來(lái)很復(fù)雜,但如果您想啟用無(wú)縫的交叉定位模型,它就是您需要的那種系統(tǒng),而這正是我們想要的。

          我們希望只在一個(gè)版本中使用術(shù)語(yǔ)“crossgen2”,之后它將替換現(xiàn)有的crossgen,然后我們將回到使用術(shù)語(yǔ)“crossgen”來(lái)表示“crossgen2”。

          .NET 診斷:EventPipe

          EventPipe 是我們用于在進(jìn)程內(nèi)或進(jìn)程外輸出事件、性能數(shù)據(jù)和計(jì)數(shù)器的跨平臺(tái)機(jī)制。從.NET 6 開始,我們已將實(shí)現(xiàn)從C++ 移至C。通過此更改,Mono 也使用EventPipe。這意味著CoreCLR 和Mono 都使用相同的事件基礎(chǔ)設(shè)施,包括.NET 診斷CLI 工具。

          這一變化還伴隨著CoreCLR 的小幅減小:

          庫(kù)

          大小之后 - 大小之前

          差異

          libcoreclr.so

          7037856 – 7049408

          -11552

          我們還進(jìn)行了一些更改,以提高 EventPipe 在負(fù)載下的吞吐量。在最初的幾個(gè)預(yù)覽版中,我們進(jìn)行了一系列更改,從而使吞吐量提高了.NET 5 的2.06 倍:

          對(duì)于這個(gè)基準(zhǔn),越高越好。.NET 6 是橙色線,.NET 5 是藍(lán)色線。

          SDK

          對(duì).NET SDK 進(jìn)行了以下改進(jìn)。

          .NET 6 SDK 可選工作負(fù)載的 CLI 安裝

          .NET 6 引入了SDK 工作負(fù)載的概念。工作負(fù)載是可選組件,可以安裝在.NET SDK 之上以啟用各種場(chǎng)景。.NET 6 中的新工作負(fù)載是:.NET MAUI 和Blazor WebAssembly AOT 工作負(fù)載。我們可能會(huì)在.NET 7 中創(chuàng)建新的工作負(fù)載(可能來(lái)自現(xiàn)有的SDK)。工作負(fù)載的最大好處是減少大小和可選性。我們希望隨著時(shí)間的推移使SDK 變得更小,并且只安裝您需要的組件。這個(gè)模型對(duì)開發(fā)者機(jī)器有好處,對(duì)CI 來(lái)說甚至更好。

          Visual Studio 用戶并不真正需要擔(dān)心工作負(fù)載。工作負(fù)載功能經(jīng)過專門設(shè)計(jì),以便像Visual Studio 這樣的安裝協(xié)調(diào)器可以為您安裝工作負(fù)載。可以通過CLI 直接管理工作負(fù)載。

          工作負(fù)載功能公開了用于管理工作負(fù)載的多個(gè)動(dòng)詞,包括以下幾個(gè):

          • dotnet workload restore— 安裝給定項(xiàng)目所需的工作負(fù)載。
          • dotnet workload install— 安裝命名工作負(fù)載。
          • dotnet workload list— 列出您已安裝的工作負(fù)載。
          • dotnet workload update— 將所有已安裝的工作負(fù)載更新到最新的可用版本。

          update動(dòng)詞查詢更新nuget.org的工作負(fù)載清單、更新本地清單、下載已安裝工作負(fù)載的新版本,然后刪除所有舊版本的工作負(fù)載。這類似于apt update && apt upgrade -y(用于基于Debian 的Linux 發(fā)行版)。將工作負(fù)載視為SDK 的私有包管理器是合理的。它是私有的,因?yàn)樗鼉H適用于SDK 組件。我們將來(lái)可能會(huì)重新考慮這一點(diǎn)。這些dotnet workload命令在給定SDK 的上下文中運(yùn)行。假設(shè)您同時(shí)安裝了.NET 6 和.NET 7。工作負(fù)載命令將為每個(gè)SDK 提供不同的結(jié)果,因?yàn)楣ぷ髫?fù)載將不同(至少相同工作負(fù)載的不同版本)。

          請(qǐng)注意,將http://NuGet.org 中的工作負(fù)載復(fù)制到您的SDK 安裝中,因此如果SDK 安裝位置受到保護(hù)(即在管理員/根位置),dotnet workload install則需要運(yùn)行提升或使用sudo

          內(nèi)置 SDK 版本檢查

          為了更容易跟蹤SDK 和運(yùn)行時(shí)的新版本何時(shí)可用,我們向.NET 6 SDK 添加了一個(gè)新命令。

          dotnet sdk check

          它會(huì)告訴您是否有可用于您已安裝的任何.NET SDK、運(yùn)行時(shí)或工作負(fù)載的更新版本。您可以在下圖中看到新體驗(yàn)。


          dotnet new

          您現(xiàn)在可以在http://NuGet.org 中搜索帶有.dotnet new --search

          模板安裝的其他改進(jìn)包括支持切換以支持私有NuGet 源的授權(quán)憑據(jù)。--interactive

          安裝CLI 模板后,您可以通過和檢查更新是否可用。--update-check--update-apply

          NuGet 包驗(yàn)證

          包驗(yàn)證工具使NuGet 庫(kù)開發(fā)人員能夠驗(yàn)證他們的包是否一致且格式正確。

          這包括:

          • 驗(yàn)證版本之間沒有重大更改。
          • 驗(yàn)證包對(duì)于所有特定于運(yùn)行時(shí)的實(shí)現(xiàn)是否具有相同的公共API 集。
          • 確定任何目標(biāo)框架或運(yùn)行時(shí)適用性差距。

          該工具是SDK 的一部分。使用它的最簡(jiǎn)單方法是在項(xiàng)目文件中設(shè)置一個(gè)新屬性。

          <EnablePackageValidation> true </EnablePackageValidation>

          更多 Roslyn 分析儀

          在.NET 5 中,我們提供了大約250 個(gè)帶有.NET SDK 的分析器。其中許多已經(jīng)存在,但作為NuGet 包在帶外發(fā)送。我們?yōu)?.NET 6 添加了更多分析器。

          默認(rèn)情況下,大多數(shù)新分析器都在信息級(jí)別啟用。您可以通過如下配置分析模式在警告級(jí)別啟用這些分析器:<AnalysisMode>All</AnalysisMode>

          我們?yōu)?NET 6 發(fā)布了我們想要的一組分析器(加上一些附加功能),然后將它們中的大多數(shù)做成了可供抓取的。社區(qū)添加了幾個(gè)實(shí)現(xiàn),包括這些。

          感謝Meik Tranel和Newell Clark。

          為 Platform Compatibility Analyzer 啟用自定義防護(hù)

          CA1416 平臺(tái)兼容性分析器已經(jīng)使用OperatingSystemRuntimeInformation中的方法識(shí)別平臺(tái)防護(hù),例如OperatingSystem.IsWindowsOperatingSystem.IsWindowsVersionAtLeast。但是,分析器無(wú)法識(shí)別任何其他保護(hù)可能性,例如緩存在字段或?qū)傩灾械钠脚_(tái)檢查結(jié)果,或者在輔助方法中定義了復(fù)雜的平臺(tái)檢查邏輯。

          為了允許自定義守衛(wèi)的可能性,我們添加了新屬性 SupportedOSPlatformGuardUnsupportedOSPlatformGuard使用相應(yīng)的平臺(tái)名稱和/或版本注釋自??定義守衛(wèi)成員。此注釋被平臺(tái)兼容性分析器的流分析邏輯識(shí)別和尊重。

          用法

              [UnsupportedOSPlatformGuard("browser")] // The platform guard attribute
          #if TARGET_BROWSER
              internal bool IsSupported => false;
          #else
              internal bool IsSupported => true;
          #endif
          
              [UnsupportedOSPlatform("browser")]
              void ApiNotSupportedOnBrowser() { }
          
              void M1()
              {
                  ApiNotSupportedOnBrowser();  // Warns: This call site is reachable on all platforms.'ApiNotSupportedOnBrowser()' is unsupported on: 'browser'
          
                  if (IsSupported)
                  {
                      ApiNotSupportedOnBrowser();  // Not warn
                  }
              }
          
              [SupportedOSPlatform("Windows")]
              [SupportedOSPlatform("Linux")]
              void ApiOnlyWorkOnWindowsLinux() { }
          
              [SupportedOSPlatformGuard("Linux")]
              [SupportedOSPlatformGuard("Windows")]
              private readonly bool _isWindowOrLinux = OperatingSystem.IsLinux() || OperatingSystem.IsWindows();
          
              void M2()
              {
                  ApiOnlyWorkOnWindowsLinux();  // This call site is reachable on all platforms.'ApiOnlyWorkOnWindowsLinux()' is only supported on: 'Linux', 'Windows'.
          
                  if (_isWindowOrLinux)
                  {
                      ApiOnlyWorkOnWindowsLinux();  // Not warn
                  }
              }
          }

          結(jié)束

          歡迎使用.NET 6。它是另一個(gè)巨大的.NET 版本,在性能、功能、可用性和安全性方面都有很多的改進(jìn)。我們希望您能找到許多改進(jìn),最終使您在日常開發(fā)中更有效率和能力,并提高性能或降低生產(chǎn)中應(yīng)用程序的成本。我們已經(jīng)開始從那些已經(jīng)開始使用.NET 6 的人那里聽到好消息。

          在Microsoft,我們還處于.NET 6 部署的早期階段,一些關(guān)鍵應(yīng)用程序已經(jīng)投入生產(chǎn),未來(lái)幾周和幾個(gè)月內(nèi)還會(huì)有更多應(yīng)用程序推出。

          .NET 6 是我們最新的LTS 版本。我們鼓勵(lì)每個(gè)人都轉(zhuǎn)向它,特別是如果您使用的是.NET 5。我們期待它成為有史以來(lái)采用速度最快的.NET 版本。

          此版本是至少1000 人(但可能更多)的結(jié)果。這包括來(lái)自Microsoft 的.NET 團(tuán)隊(duì)以及社區(qū)中的更多人。我試圖在這篇文章中包含許多社區(qū)貢獻(xiàn)的功能。感謝您抽出寶貴時(shí)間創(chuàng)建這些內(nèi)容并完成我們的流程。我希望這次經(jīng)歷是一次美好的經(jīng)歷,并且更多的人會(huì)做出貢獻(xiàn)。

          這篇文章是許多有才華的人合作的結(jié)果。貢獻(xiàn)包括團(tuán)隊(duì)在整個(gè)發(fā)布過程中提供的功能內(nèi)容、為此最終帖子創(chuàng)建的重要新內(nèi)容,以及使最終內(nèi)容達(dá)到您應(yīng)得的質(zhì)量所需的大量技術(shù)和散文更正。很高興為您制作它和所有其他帖子。

          感謝您成為.NET 開發(fā)人員。


          主站蜘蛛池模板: 国产精品一区二区AV麻豆| 人妻夜夜爽天天爽一区| 亚洲欧洲一区二区| 综合久久久久久中文字幕亚洲国产国产综合一区首 | 精品国产aⅴ无码一区二区| 91精品福利一区二区| 麻豆国产在线不卡一区二区| 国产免费av一区二区三区| 国产一区二区在线观看麻豆| 中文字幕亚洲一区二区va在线| 91久久精品无码一区二区毛片 | 韩国精品一区视频在线播放 | 国产丝袜美女一区二区三区 | 久久精品国产亚洲一区二区三区| 国产在线精品一区二区三区直播| 亚洲AV成人一区二区三区AV| 国模一区二区三区| 国产精品无码一区二区三级| 亚洲精品色播一区二区| 久久精品无码一区二区三区日韩| 国产免费一区二区视频| 人妻夜夜爽天天爽一区| 久久99精品波多结衣一区| 日本在线电影一区二区三区| 国产激情无码一区二区app| 国产精品日韩一区二区三区| 日本成人一区二区三区| 国产福利一区二区精品秒拍| 亚洲国产国产综合一区首页| 日本精品夜色视频一区二区 | 精品人妻一区二区三区四区 | 精品乱子伦一区二区三区高清免费播放 | 无码视频免费一区二三区| 香蕉在线精品一区二区| 日韩一区二区超清视频| 国产福利精品一区二区| 亚洲日本一区二区三区在线不卡| 中文字幕精品一区影音先锋| 亚洲国产精品一区二区三区久久| 亚洲欧洲无码一区二区三区| 91精品一区二区三区在线观看|