日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET Core跨平台的奥秘[下篇]:全新的布局

發(fā)布時間:2023/12/4 asp.net 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET Core跨平台的奥秘[下篇]:全新的布局 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

從本質(zhì)上講,按照CLI規(guī)范設(shè)計的.NET從其出生的那一刻就具有跨平臺的基因,這與Java別無二致。由于采用了統(tǒng)一的中間語言,微軟只需要針對不同的平臺設(shè)計不同的虛擬機(運行時)就能彌合不同操作系統(tǒng)與處理器架構(gòu)之間的差異,但是“理想很豐滿,現(xiàn)實很骨感”。在過去十多年中,微軟將.NET引入到了各個不同的應(yīng)用領(lǐng)域,表面上看起來似乎欣欣向榮,但是由于采用完全獨立的多目標(biāo)框架的設(shè)計思路,導(dǎo)致針對多目標(biāo)框架的代碼平臺只能通過PCL(參考《.NET Core跨平臺的奧秘[中篇]:復(fù)用之殤》)這種“妥協(xié)”的方式來解決。如果依然按照這條道路走下去,.NET的觸角延伸得越廣,枷鎖將越來越多,所以.NET 已經(jīng)到了不得不做出徹底改變的時刻了。

一、跨平臺的.NET Core

綜上所述,要真正實現(xiàn).NET 的跨平臺偉業(yè),主要需要解決兩個問題,一是針對不同的平臺設(shè)計相應(yīng)的運行時為中間語言CIL提供一個一致性的執(zhí)行環(huán)境,而是提供統(tǒng)一的BCL以徹底解決代碼復(fù)用的難題。對于真正跨平臺的.NET Core來說,微軟不僅為它設(shè)計了針對不同平臺被成為CoreCLR的運行時,同時還重新設(shè)計了一套被稱為CoreFX的BCL。

如上圖所示,NET Core目前支持的AppModel主要有兩種,其中ASP.NET Core用于開發(fā)服務(wù)器Web應(yīng)用和服務(wù),而UWP(Universal Windows Platform)則用于開發(fā)能夠在各種客戶端設(shè)備(Mobile、PC、Xbox、Devices + IOT、HoloLens和Surface Hub等)上以自適應(yīng)方式運行的Windows 10應(yīng)用。CoreFX是經(jīng)過完全重寫的BCL,除了自身就具有跨平臺執(zhí)行的能力之外,其提供的API也不再是統(tǒng)一定義在少數(shù)幾個單一的程序集中,而是經(jīng)過有效分組之后被定義在各自獨立的模塊中。這些模塊對應(yīng)著一個單一的程序集,并最終由對應(yīng)的NuGet包來分發(fā)。至于底層的虛擬機,微軟則為主流的操作系統(tǒng)類型(Windows、Mac OS X和Linux)和處理器架構(gòu)(x86、x64和ARM)設(shè)計了針對性的運行時,被稱為CoreCLR。

作為運行時的CoreCLR和提供BCL的CoreFX是.NET Core兩根重要的基石,但是就開發(fā)成本來看,微軟在后者投入的精力是前者無法比擬的。我們知道.NET Core自誕生到現(xiàn)在已經(jīng)有好些年了,目前的版本還只是到了2.0,從發(fā)布進度上顯得稍顯緩慢,其中一個主要的原因是:重寫CoreFX提供的基礎(chǔ)API確實是一件繁瑣耗時的工程,而且這項工程遠(yuǎn)未結(jié)束。為了對CoreFX提供的BCL有一個大致的了解,我們看看這些常用的基礎(chǔ)API究竟定義在哪些命名空間下。

  • System.Collections:定義了我們常用的集合類型。

  • System.Console:提供API完成基本的控制臺操作。

  • System.Data:提供用于訪問數(shù)據(jù)庫的API,相當(dāng)于原來的ADO.NET。

  • System.Diagnostics:提供基本的診斷、調(diào)試和追蹤的API。

  • System.DirectoryServices:提供基于AD(Active Directory)管理的API。

  • System.Drawing:提供GDI相關(guān)的API。

  • System.Globalization:提供API實現(xiàn)多語言以及全球化支持。

  • System.IO:提供針對文件輸入輸出相關(guān)的API。

  • System.Net:提供與網(wǎng)絡(luò)通信相關(guān)的API。

  • System.Reflection:提供API以實現(xiàn)與反射相關(guān)的操作。

  • System.Runtime:提供與運行時相關(guān)的一些基礎(chǔ)類型。

  • System.Security:提供與數(shù)據(jù)簽名和加解密相關(guān)的API。

  • System.Text:提供針對字符串/文本編碼與解碼相關(guān)的API。

  • System.Threading:提供用于管理線程的API。

  • System.Xml:提供API用以操作XML結(jié)構(gòu)的數(shù)據(jù)。

我們知道對于傳統(tǒng)的.NET Framework來說,承載BCL的API幾乎都定義在mscorlib.dll這個程序集中,這些API并不是全部都轉(zhuǎn)移到組成CoreFX的眾多程序集中,那些與運行時(CoreCLR)具有緊密關(guān)系的底層API被定義到一個叫做System.Private.CoreLib.dll的程序集中,所以下圖反映了真正的.NET Core層次結(jié)構(gòu)。我們在編程過程中使用的基礎(chǔ)數(shù)據(jù)類型基本上都定義在這個程序集中,所以目前這個程序集的尺寸已經(jīng)超過了10M。由于該程序集提供的API與運行時關(guān)聯(lián)較為緊密,較之CoreFX提供的API,這些基礎(chǔ)API具有較高的穩(wěn)定性,所以它是隨著CoreCLR一起發(fā)布的。

雖然我們編程過程中使用到的絕大部分基礎(chǔ)類型都定義在System.Private.CoreLib.dll程序集中,但是這卻是一個“私有”的程序集,我們可以從其命名看出這一點。我們將System.Private.CoreLib.dll稱為一個私有程序集,并不是說定義其中的都是一些私有類型,而是因為我們在編程的過程不會真正引用這個程序集,這與.NET Framework下的mscorlib.dll是不一樣的。不僅如此,當(dāng)我們編寫的.NET Core代碼被編譯的時候,編譯器也不會鏈接到這個程序集上,也就是說編譯后生成的程序集中同樣也沒有針對該程序集引用的元數(shù)據(jù)。但是當(dāng)我們的應(yīng)用被真正執(zhí)行的時候,所有引用的基礎(chǔ)類型全部會自動 “轉(zhuǎn)移” 到這個程序集中。至于如何實現(xiàn)運行過程中的類型轉(zhuǎn)移,其實就是利用了我們上面介紹的Type Forwarding技術(shù)。

實例演示:針對System.Private.CoreLib.dll程序集的類型轉(zhuǎn)移

對上面介紹的針對System.Private.CoreLib.dll程序集的類型轉(zhuǎn)移,可能很多人還是難以理解,為了讓大家對這個問題具有徹底的認(rèn)識,我們不妨來做一個簡單的實例演示。我們利用Visual Studio創(chuàng)建一個.NET Core控制臺應(yīng)用,并在作為程序入口的Main方法中編寫如下幾行代碼,它們會將我們常用的幾個數(shù)據(jù)類型(System.String、System.Int32和System.Boolean)所在的程序集名稱打印在控制臺上。

? 1: class Program ? 2: { ? 3:???? static?void Main() ? 4:???? { ? 5:???????? Console.WriteLine(typeof(string).Assembly.FullName); ? 6:???????? Console.WriteLine(typeof(int).Assembly.FullName); ? 7:???????? Console.WriteLine(typeof(bool).Assembly.FullName); ? 8:???? } ? 9: }

根據(jù)我們上面的分析,程序運行過程中使用到的這些基礎(chǔ)類型全部來源于System.Private.CoreLib.dll這個程序集中,關(guān)于這一點在如下圖所示的輸出結(jié)果中得到了證實。我們通過圖2-24所示的輸出結(jié)果,我們不僅僅知道了這個核心程序集的名稱,還知道了該程序集目前的版本(4.0.0.0);

我們說應(yīng)用編譯后生成的程序集并不會具有針對System.Private.CoreLib.dll程序集引用的元數(shù)據(jù),為了證明這一點,我們只需要利用Windows SDK(在目錄“%ProgramFiles(x86)%Microsoft SDKs\Windows\{version}\Bin”下)提供的反編譯工具ildasm.exe就可以了。利用ildasm.exe打開這個控制臺應(yīng)用編譯后生成的程序集之后,我們會發(fā)現(xiàn)它具有如下這兩個程序集的應(yīng)用。

? 1: .assembly extern?System.Runtime ? 2: { ? 3:?? .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )???????????????????????? ? 4:?? .ver 4:2:0:0 ? 5: } ? 6: .assembly extern System.Console ? 7: { ? 8:?? .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )???????????????????????? ? 9:?? .ver 4:1:0:0 ?10: }

實際上我們的程序只涉及到四個類型,即一個Console類型和三個基礎(chǔ)數(shù)據(jù)類型(String、Int32和Boolean),而程序集層面則只有針對System.Runtime和System.Console程序集的引用,那么毫無疑問,后面這三個數(shù)據(jù)類型肯定與System.Runtime程序集有關(guān),那么該程序集針對這三個數(shù)據(jù)類型具有怎樣的定義呢?為了得到答案,我們先得知道這個程序集究竟被保存在哪里。我們知道“%ProgramFiles%dotnet\”是.NET Core的應(yīng)用根目錄,而這個System.Runtime.dll作為“共享”程序集被保存在子目錄“\shared\Microsoft.NETCore.App\2.0.0”下面,這個目錄下面還保存著很多其他的共享程序集。

我們依然利用反編譯工具ildasm.exe查看System.Runtime.dll程序集清單文件的元數(shù)據(jù)定義。我們會發(fā)現(xiàn)整個程序集除了定義少數(shù)幾個核心類型(比如兩個重要的委托類型Action和Func就定義在這個程序集中),它的作用就是將所有基礎(chǔ)的類型采用Type Forwarding的方式轉(zhuǎn)移到System.Private.CoreLib.dll程序集中,下面的代碼片段為你展示了針對我們程序使用的三個基礎(chǔ)數(shù)據(jù)類型轉(zhuǎn)移的相關(guān)定義。

? 1: .assembly extern System.Private.CoreLib ? 2: { ? 3:?? .publickeytoken = (7C EC 85 D7 BE A7 79 8E )???????? ? 4:?? .ver 4:0:0:0 ? 5: } ? 6: .class?extern?forwarder System.String ? 7: { ? 8:?? .assembly extern?System.Private.CoreLib ? 9: } ?10: .class?extern?forwarder System.Int32 ?11: { ?12:?? .assembly extern?System.Private.CoreLib ?13: } ?14: .class?extern?forwarder System.Boolean ?15: { ?16:?? .assembly extern?System.Private.CoreLib ?17: }

我們演示實例體現(xiàn)的程序集直接的引用關(guān)系,以及如上代碼片段體現(xiàn)的相關(guān)基礎(chǔ)類型(System.String、System.Int32和System.Boolean)的轉(zhuǎn)移方向基本體現(xiàn)在如下圖所示的關(guān)系圖中。

復(fù)用.NET Framework程序集

我們將上述這種利用Type Forwarding方式實現(xiàn)跨程序集類型轉(zhuǎn)移的技術(shù)成為“墊片(Shim)”,這是實現(xiàn)程序集跨平臺復(fù)用的重要手段。除了System.Runtime.dll,.NET Core還提供了其他一些其他墊片程序集,正是源于這這些墊片程序集的存在,我們可以將在.NET Framework環(huán)境下編譯的程序集在.NET Core應(yīng)用中使用。為了讓讀者朋友們對此有深刻的認(rèn)識,我們照例來做一個簡單的實例演示。

我們利用Visual Studio創(chuàng)建一個空的解決方案,并添加如下三個項目(NetApp、NetCoreApp、NetLib),其中NetApp和NetCoreApp分別是針對.NET Framework(4.7)和.NET Core(2.0)的控制臺程序,而NetLib則是針對.NET Framework的類庫項目,該項目定義的API將在NetApp和NetCoreApp被調(diào)用。

我們在NetLib項目中定義了一個Utils工具類,并在其中定義了一個PrintAssemblyNames方法。如下面的代碼片段所示,我們在這個方法中打印出三個常用的類型(Task、Uri和XmlWriter)所在的程序集的名稱。通過在不同類型(.NET Framework和.NET Core)的應(yīng)用中調(diào)用這個方法,我們就可以確定它們在運行時究竟是從那個程序集中加載的。我們分別在NetApp和NetCoreApp這兩個不同類型的控制臺程序中調(diào)用了這個方法。

NetLib:

? 1: public?class Utils ? 2: { ? 3:???? public?static?void PrintAssemblyNames() ? 4:???? {?????????? ? 5:???????? Console.WriteLine(typeof(Task).Assembly.FullName); ? 6:???????? Console.WriteLine(typeof(Uri).Assembly.FullName); ? 7:???????? Console.WriteLine(typeof(XmlWriter).Assembly.FullName); ? 8:???? } ? 9: }

NetApp:

? 1: class Program ? 2: { ? 3:???? static?void Main() ? 4:???? { ? 5:???????? Console.WriteLine(".NET Framework 4.7"); ? 6:???????? Utils.PrintAssemblyNames(); ? 7:???? } ? 8: }

NetCoreApp:

? 1: class Program ? 2: { ? 3:???? static?void Main() ? 4:???? { ? 5:???????? Console.WriteLine(".NET Core 2.0"); ? 6:???????? Utils.PrintAssemblyNames(); ? 7:???? } ? 8: }

直接運行NetApp和NetCoreApp這兩個控制臺程序后,我們會發(fā)現(xiàn)不同的輸出結(jié)果。如下圖所示,對于我們指定的三個類型(System.Threading.Tasks.Task、System.Uri和System.Xml.XmlWriter),分別在.NET Framework和.NET Core環(huán)境下承載它們的程序集是不同的。具體來說,.NET Framework環(huán)境下的這三個類型分別定義在mscorlib.dll、System.dll和System.Xml.dll中;當(dāng)切換到.NET Core環(huán)境下后,運行時則會從三個私有的程序集System.Private.CoreLib.dll、System.Private.Uri.dll和System.Private.Xml.dll中加載這三個類型。

由于NetApp和NetCoreApp這兩個控制臺應(yīng)用使用的都是同一個針對.NET Framework編譯的程序集NetLib.dll,所以我們先利用反編譯工具ildasm.exe查看一下它具有怎樣的程序集引用。如下面的代碼片段所示,程序集NetLib.dll引用的程序集與控制臺應(yīng)用NetApp的輸出結(jié)果是一致的。

? 1: .assembly extern?mscorlib ? 2: { ? 3:?? .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )???????????????????????? ? 4:?? .ver 4:0:0:0 ? 5: } ? 6: .assembly extern System ? 7: { ? 8:?? .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )???????????????????????? ? 9:?? .ver 4:0:0:0 ?10: } ?11: .assembly extern?System.Xml ?12: { ?13:?? .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )???????????????????????? ?14:?? .ver 4:0:0:0 ?15: }

那么我們的核心問題變成了:Task、Uri和XmlWriter這三個類型在.NET Core的運行環(huán)境下是如何轉(zhuǎn)移到其他程序集中的。要回答這個問題,我們只需要利用ildasm.exe查看mscorlib.dll、System.dll和System.Xml.dll反編譯這三個程序集就可以了。這三個程序集同樣存在于“%ProgramFiles%dotnet\\shared\Microsoft.NETCore.App\2.0.0”目錄下,通過反編譯與它們相關(guān)的程序集,我們得到如下所示的相關(guān)元數(shù)據(jù)。

mscorlib.dll

? 1: .assembly extern System.Private.CoreLib ? 2: { ? 3:?? .publickeytoken = (7C EC 85 D7 BE A7 79 8E )???????????????????????? ? 4:?? .ver 4:0:0:0 ? 5: } ? 6: .class?extern forwarder System.Threading.Tasks.Task ? 7: { ? 8:?? .assembly extern?System.Private.CoreLib ? 9: }

System.dll

? 1: .assembly extern System.Private.Uri ? 2: { ? 3:?? .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )???????????????????????? ? 4:?? .ver 4:0:4:0 ? 5: } ? 6: .class?extern forwarder System.Uri ? 7: { ? 8:?? .assembly extern?System.Private.Uri ? 9: }

System.Xml.dll

? 1: .assembly extern System.Xml.ReaderWriter ? 2: { ? 3:?? .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )???????????????????????? ? 4:?? .ver 0:0:0:0 ? 5: } ? 6: .class?extern forwarder System.Xml.XmlWriter ? 7: { ? 8:?? .assembly extern?System.Xml.ReaderWriter ? 9: }

System.Xml.ReaderWriter.dll

? 1: .assembly extern System.Private.Xml ? 2: { ? 3:?? .publickeytoken = (CC 7B 13 FF CD 2D DD 51 )???????????????????????? ? 4:?? .ver 4:0:0:0 ? 5: } ? 6: .class?extern forwarder System.Xml.XmlWriter ? 7: { ? 8:?? .assembly extern?System.Private.Xml ? 9: }

如上面的代碼片段所示,針對Task、Uri和XmlWriter這三個類型的轉(zhuǎn)移一共涉及到七個程序集,其中mscorlib.dll、System.dll和System.Xml.dll是NetLib.dll直接引用的三個程序集,而System.Private.CoreLib.dll、System.Private.Uri.dll和System.Private.Xml.dll則是最終承載這三個類型的程序集。對于Task和Uri類型來說,它們只經(jīng)歷一次轉(zhuǎn)移,而XmlWriter則經(jīng)歷了兩次類型轉(zhuǎn)移,它轉(zhuǎn)移到程序集System.Xml.ReaderWriter.dll中,再借助后者轉(zhuǎn)移到目標(biāo)程序集System.Private.Xml.dll,程序集引用和類型轉(zhuǎn)移關(guān)系體現(xiàn)在下圖中。

二、多平臺復(fù)用的BCL

雖然.NET Core借助于CoreCLR和CoreFX實現(xiàn)了真正的跨平臺,但是目前的.NET Core僅僅提供ASP.NET Core和UWP這兩種編程模型,雖然后者旨在實現(xiàn)多種設(shè)備的統(tǒng)一編程,但依然還是關(guān)注于Windows平臺。對于傳統(tǒng).NET Framework下面向桌面應(yīng)用的WPF和Windows Forms,它們并沒有跨平臺的意義,所以依然是今后.NET的一大分支。除此之外,雖然我們有了跨平臺的ASP.NET Core,傳統(tǒng)的ASP.NET依然被保留了下來,并且在今后一段時間內(nèi)還將繼續(xù)升級。除了.NET Framework和.NET Core,.NET還具有另一個重要的分支,那就是Xamarin,它可以幫助我們?yōu)閕OS、OS X和Android編寫統(tǒng)一的應(yīng)用。在.NET誕生十多年后,微軟開始對.NET進行了全新的布局,建立了 “大一統(tǒng)” 的.NET平臺。總的來說,這個所謂的大一統(tǒng).NET平臺由如下圖所示的.NET Framework、.NET Core和Xamarin這三個分支組成。

雖然被微軟重新布局的.NET平臺只包含了三個分支,但是之前遇到的一個重要的問題依然存在,那就是代碼的復(fù)用,說的更加具體的是應(yīng)該是程序集的復(fù)用而不是源代碼的復(fù)用。我們知道之前解決程序集服務(wù)的方案就是PCL,但這并不是一種理想的解決方案,由于各個目標(biāo)框架具有各種獨立的BCL,所以我們創(chuàng)建的PCL項目只能建立在指定的幾種兼容目標(biāo)框架的BCL交集之上。對于全新的.NET平臺來說,這個問題通過提供統(tǒng)一的BCL得到根本的解決,這個統(tǒng)一的BCL被稱為.NET Standard

我們可以將.NET Standard稱為新一代的PCL,PCL提供的可移植能力僅僅限于創(chuàng)建時就確定下來的幾種目標(biāo)平臺,但是.NET Standard做得更加徹底,因為它在設(shè)計的時候就已經(jīng)考慮針對三大分支的復(fù)用。如下圖所示,.NET Standard為.NET Framework、.NET Core和Xamarin提供了統(tǒng)一的API,那么我們在這組標(biāo)準(zhǔn)API基礎(chǔ)上編寫的代碼自然就能被所有類型的.NET應(yīng)用復(fù)用。

.NET Standard提供的API主要是根據(jù)現(xiàn)有.NET Framework來定義的,它的版本升級反映了其提供的API不斷豐富的過程,目前最新版本(.NET Standard 2.0)提供的API數(shù)量在前一版本基礎(chǔ)上幾乎翻了一番。Visual Studio提供相應(yīng)的項目模板幫助我們創(chuàng)建基于.NET Standard的類庫項目,這樣的項目會采用專門的目標(biāo)框架別名netstandard{version}。一個針對.NET Standard 2.0的類庫項目具有如下的定義,我們可以看到它采用的目標(biāo)框架別名為 “.NET Standard 2.0” 。

? 1: <Project Sdk="Microsoft.NET.Sdk"> ? 2:?? <PropertyGroup> ? 3:???? <TargetFramework>netstandard2.0</TargetFramework> ? 4:?? </PropertyGroup> ? 5: </Project>

顧名思義,.NET Standard僅僅是一個標(biāo)準(zhǔn),而不提供具體的實現(xiàn)。我們可以簡單理解為.NET Standard為我們定義了一整套標(biāo)準(zhǔn)的接口,各個分支需要針對自身的執(zhí)行環(huán)境對這套接口提供實現(xiàn)。對于.NET Core來說,它的基礎(chǔ)API主要由CoreFX和System.Private.CoreLib.dll這個核心程序集來承載,這些API基本上就是根據(jù).NET Standard來設(shè)計的。但是對.NET Framework來說,它的BCL提供的API與.NET Standard存在著很大的交集,實際上.NET Standard基本上就是根據(jù).NET Framework現(xiàn)有的API來設(shè)計的,所以微軟不可能在.NET Framework上重寫一套類型于CoreFX的實現(xiàn),只需要采用某個技術(shù) “鏈接” 到現(xiàn)有的程序集上就可以了。

一個針對.NET Standard編譯生成的程序集在不同的執(zhí)行環(huán)境中針對真正提供實現(xiàn)的程序集的所謂“鏈接”依然是通過上面我們介紹的“墊片”技術(shù)來實現(xiàn)的,為了徹底搞清楚這個問題,我們還是先來作一個簡單的實例演示。如下圖所示,我們創(chuàng)建了與上面演示實例具有類似結(jié)構(gòu)的解決方案,與之不同的是,分別針對.NET Framework和.NET Core的控制臺應(yīng)用NetApp和NetCoreApp共同引用的類庫NetStandardLib是一個.NET Standard 2.0類庫項目。

與上面演示的實例一樣,我們在NetStandardLib中定義了如下一個Utils類,并利用定義其中的靜態(tài)方法PrintAssemblyNames數(shù)據(jù)兩個數(shù)據(jù)類型(Dictionary<,>和SortedDictionary<,>)所在的程序集名稱,該方法分別在NetApp和NetCoreApp的入口Main方法中被調(diào)用。

NetStandardLib:

? 1: public?class Utils ? 2: { ? 3:???? public?static?void PrintAssemblyNames() ? 4:???? {?????????? ? 5:???????? Console.WriteLine(typeof(Dictionary<,>).Assembly.FullName); ? 6:???????? Console.WriteLine(typeof(SortedDictionary<,>).Assembly.FullName); ? 7:???? } ? 8: }

NetApp:

? 1: class Program ? 2: { ? 3:???? static?void Main() ? 4:???? { ? 5:???????? Console.WriteLine(".NET Framework 4.7"); ? 6:???????? Utils.PrintAssemblyNames(); ? 7:???? } ? 8: }

NetCoreApp:

? 1: class Program ? 2: { ? 3:???? static?void Main() ? 4:???? { ? 5:???????? Console.WriteLine(".NET Core 2.0"); ? 6:???????? Utils.PrintAssemblyNames(); ? 7:???? } ? 8: }

直接運行這兩個分別針對.NET Framework和.NET Core的控制臺應(yīng)用NetApp和NetCoreApp,我們會發(fā)現(xiàn)它們會生成不同的輸出結(jié)果。如下圖所示,在.NET Framework和.NET Core 執(zhí)行環(huán)境下,Dictionary<,>和SortedDictionary<,>這另個泛型字典類型其實來源于不同的程序集。具體來說,我們常用的Dictionary<,>類型在.NET Framework 4.7和.NET Core 2.0環(huán)境下分別定義在程序集mscorlib.dll和System.Private.CoreLib.dll中,而SortedDictionary<,>所在的程序集則分別是System.dll和System.Collection.dll。

對于演示的這個實例來說,這個NetStandardLib類庫項目針對的目標(biāo)框架為.NET Standard 2.0,后者最終體現(xiàn)為一個名為NetStandard.Library.nupkg的NuGet包,這一點其實可以從Visual Studio針對該項目的依賴節(jié)點可以看出來。如下圖所示,這個名為NetStandard.Library的NuGet包具有一個核心的程序集netstandard.dll,上面我們所說的.NET Standard API就定義在該程序集中。

也就是說,所有.NET Standard 2.0項目都具有針對程序集netstandard.dll的依賴,這個依賴自然也會體現(xiàn)在編譯后生成的程序集上。對于我們演示實例中的這個類庫項目NetStandardLib編譯生成的同名程序集來說,它針對程序集netstandard.dll的依賴體現(xiàn)在如下所示的元數(shù)據(jù)中。

? 1: .assembly extern?netstandard ? 2: { ? 3:?? .publickeytoken = (CC 7B 13 FF CD 2D DD 51 )???????????????????????? ? 4:?? .ver 2:0:0:0 ? 5: } ? 6: .assembly NetStandardLib ? 7: { ? 8:?? ... ? 9: } ?10: ...

按照我們即有的知識,原本定義在netstandard.dll的兩個類型(Dictionary<,>和SortedDictionary<,>)在不同過的執(zhí)行環(huán)境中需要被轉(zhuǎn)移到另一個程序集中,我們完全可以在相應(yīng)的環(huán)境中提供一個同名的墊片程序集并借助類型的跨程序集轉(zhuǎn)移機制來實現(xiàn),實際上微軟也就是這么做的。我們先來看看針對.NET Framework的墊片程序集netstandard.dll的相關(guān)定義,我們可以直接在NetApp編譯的目標(biāo)目錄中找到這個程序集。借助于反編譯工具ildasm.exe,我們可以很容易地得到與Dictionary<,>和SortedDictionary<,>這兩個泛型字典類型轉(zhuǎn)移的相關(guān)元數(shù)據(jù),具體的內(nèi)容下面的代碼片段所示。

? 1: .assembly extern mscorlib ? 2: { ? 3:?? .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )???????????????????????? ? 4:?? .ver 0:0:0:0 ? 5: } ? 6: .assembly extern System ? 7: { ? 8:?? .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )???????????????????????? ? 9:?? .ver 0:0:0:0 ?10: } ?11: .class?extern forwarder System.Collections.Concurrent.ConcurrentDictionary`2 ?12: { ?13:?? .assembly extern mscorlib ?14: } ?15: .class?extern forwarder System.Collections.Generic.SortedDictionary`2 ?16: { ?17:?? .assembly extern System ?18: }

針對.NET Core的墊片程序集netstandard.dll被保存在我們前面提到的共享目錄“%ProgramFiles%dotnet\shared\Microsoft.NETCore.App\2.0.0”下,我們采用同樣的方式提取出與Dictionary<,>和SortedDictionary<,>這兩個泛型字典類型轉(zhuǎn)移的元數(shù)據(jù)。從如下的代碼片段我們可以清晰地看出,Dictionary<,>和SortedDictionary<,>這兩個類型都被轉(zhuǎn)移到程序集System.Collections.dll之中。

? 1: .assembly extern System.Collections ? 2: { ? 3:?? .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )???????????????????????? ? 4:?? .ver 0:0:0:0 ? 5: } ? 6: .class?extern forwarder System.Collections.Generic.Dictionary`2 ? 7: { ? 8:?? .assembly extern System.Collections ? 9: } ?10: .class?extern forwarder System.Collections.Generic.SortedDictionary`2 ?11: { ?12:?? .assembly extern System.Collections ?13: }

從演示實例的執(zhí)行結(jié)果我們知道,SortedDictionary<,>確實是定義在程序集System.Collections.dll中,但是我們常用的Dictionary<,>類型則出自核心程序集System.Private.CoreLib.dll,那么我們可以斷定Dictionary<,>類型在System.Collections.dll中必然出現(xiàn)了二次轉(zhuǎn)移。為了確認(rèn)我們的斷言,我們只需要采用相同的方式反編譯程序集System.Collections.dll,該程序集也被存儲在共享目錄 “%ProgramFiles%dotnet\shared\Microsoft.NETCore.App\2.0.0” 中,該程序集中針對Dictionary<,>類型的轉(zhuǎn)移體現(xiàn)在如下所示的元數(shù)據(jù)中。

? 1: .assembly extern System.Private.CoreLib ? 2: { ? 3:?? .publickeytoken = (7C EC 85 D7 BE A7 79 8E )???????????????????????? ? 4:?? .ver 4:0:0:0 ? 5: } ? 6: .class?extern forwarder System.Collections.Generic.Dictionary`2 ? 7: { ? 8:?? .assembly extern System.Private.CoreLib ? 9: }

上面針對Dictionary<,>和SortedDictionary<,>這兩個類型分別在.NET Framework 4.7和.NET Core環(huán)境下的跨程序集轉(zhuǎn)移路徑基本上體現(xiàn)在下圖之中。簡單來說,.NET Framework環(huán)境下的墊片程序集netstandard.dll將這兩個類型分別轉(zhuǎn)移到了程序集mscorlib.dll和System.dll之中。如果執(zhí)行環(huán)境切換到了.NET Core,這兩個類型先被轉(zhuǎn)移到System.Collection.dll中,但是Dictionary<,>這個常用類型最終是由System.Private.CoreLib.dll這個基礎(chǔ)程序集承載的,所有System.Collection.dll中針對該類型作了二次轉(zhuǎn)移。

上面這個簡單的類型基本上揭示了.NET Standard為什么能夠提供全平臺的可移植性,我們現(xiàn)在來對此做一個簡單的總結(jié)。.NET Standard API由NetStandard.Library這個NuGet包來承載,后者提供了一個名為netstandard.dll的程序集,保留在這個程序集中的僅僅是. NET Standard API的存根(Stub),而不提供具體的實現(xiàn)。所有對于一個目標(biāo)框架為.NET Standard的類庫項目編譯生成的程序集來說,它們保留了針對程序集netstandard.dll的引用。

.NET平臺的三大分支(.NET Framework、.NET Core和Xamarin)按照自己的方式各自實現(xiàn)了.NET Standard規(guī)定的這套標(biāo)準(zhǔn)的API。由于在運行時真正承載.NET Standard API的類型被分布到多個程序集中,所以. NET Standard程序集能夠被復(fù)用的前提是運行時能夠?qū)⑦@些基礎(chǔ)類型鏈接到對應(yīng)的程序集上。由于. NET Standard程序集是針對netstandard.dll進行編譯的,所以我們只需要在各自環(huán)境中提供這個同名的程序集來完成類型的轉(zhuǎn)移即可。

.NET Core跨平臺的奧秘[上篇]:歷史的枷鎖
.NET Core跨平臺的奧秘[中篇]:復(fù)用之殤
.NET Core跨平臺的奧秘[下篇]:全新的布局

原文地址:http://www.cnblogs.com/artech/p/how-to-cross-platform-03.html


.NET社區(qū)新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com

總結(jié)

以上是生活随笔為你收集整理的.NET Core跨平台的奥秘[下篇]:全新的布局的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。