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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET 之路 | 007 详解 .NET 程序集

發布時間:2024/9/3 asp.net 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET 之路 | 007 详解 .NET 程序集 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  上一篇我們介紹了 Roslyn 編譯器,我們知道,我們編寫的 C#/VB 代碼經過 Roslyn 編譯器編譯后會生成程序集文件。按照之前講的 .NET 執行模型的順序,這一篇我具體講講程序集。

  什么是程序集

  我們編寫的 C# 代碼經過編譯會生成 .dll 或 .exe 文件,這些文件就是 .NET 的程序集(Assembly)。

  盡管 .NET 的程序集文件與非托管的 Windows 二進制文件采用相同的文件擴展名(*.dll),但它們的內部完全不同。具體來說,.NET Core 程序集文件不包含平臺(泛指操作系統和 CPU 架構的組合)特定的指令,而是平臺無關的中間語言(IL)和類型元數據。

  你可能在一些 /.NET Core 的文檔中看到過 IL 的另外兩種縮寫:MSIL(Microsoft Intermediate Language,微軟中間語言) 和 CIL(Common Intermediate Language,通用中間語言)。IL、MSIL 和 CIL 都是一個概念,其中 MSIL 是早期的叫法,現在已經很少有人用了。

  但 .NET Core 與 .NET Framework 不一樣,.NET Core 始終只會生成 *.dll 格式的程序集文件,即使是像控制臺應用這樣的可執行項目也不再會生成 *.exe 格式的程序集文件。

  那我們在 .NET Core 項目的 bin 目錄中看到和項目同名的 *.exe 文件是怎么回事呢?這個文件并不是一個程序集文件,而是專門為 Windows 平臺生成的一個可執行的快捷方式。在 Windows 平臺雙擊這個文件等同于執行 dotnet?.dll 命令。在我們安裝的 .NET Core 目錄中有個 dotnet.exe 命令文件(如 Windows 系統默認位置是C:\Program Files\dotnet\dotnet.exe),在編譯時,該文件會被復制到構建目錄,并重命名為與項目名稱同名的?.exe 文件。

  程序集的組成

  總的來說,每個程序集文件主要由 IL 代碼、元數據(Metadata)、清單(Manifest) 和資源文件(如 jpg、html、txt 等)組成。其中,IL 代碼和元數據會先被編譯為一個或多個托管模塊,然后托管模塊和資源文件會被合并成程序集。

  

  托管模塊,或者叫托管資源或托管代碼,顧名思義,這種資源是由 .NET Core 的 CLR 運行時來管理運行的,它包含 IL 代碼和元數據。比如對象的回收是由 CLR 中垃圾回收器(GC)自動執行的,不需要手動管理。

  程序集文件中占比最大的一般是 IL 代碼。IL 代碼和 Java 字節碼相似,它不包含平臺特定的指令,它只在必要的時候被 .NET Core 運行時中的 JIT 編譯器編譯成本機代碼(機器碼)。

  程序集文件中的元數據詳細地描述了程序集文件中每個類型的特征。比如有一個名為 Product 的類,類型元數據描述了 Product 的基類、實現的接口(如果有的話)和每個成員的完整描述等細節。元數據由語言編譯器(Roslyn)自動生成。

  除了托管模塊,程序集文件還可以嵌入資源文件,如 jpg、gif、html 等格式的靜態文件,這些文件是非托管資源。

  當托管模塊和資源文件合并成程序集時,會生成一份清單,它是專門用來描述程序集本身的元數據。清單包含程序集的當前版本信息、本地化信息(用于本地化字符串等),以及正確執行所需的所有外部引用程序集列表等。

  在第 5 篇文章中我們講了 .NET 的兩種執行模型,其中,當基于本地運行時執行模型發布時,雖然你的應用程序可以發布為可直接執行的單一文件,但這個單一的文件其實是多個文件的包裝。它包含了由 IL 代碼編譯成的本地代碼和 Native AOT 本地運行時。你的代碼仍然在一個托管的容器中運行,運行時它的資源的管理和它作為多個文件發布是一樣的。

  下面讓我們更詳細地了解一下 IL 代碼、元數據和程序集清單。

  IL 代碼

  我們先來看看下面這樣一段簡單的 C# 代碼被編譯成 IL 代碼會是什么樣子。C# 代碼如下:

  class Calculator

  {

  public int Add(int num1,int num2)

  {

  return num1 + num2;

  }

  }

  經過編譯后,在項目的 bin\Debug 目錄會生成一個與項目名稱同名的 dll 程序集文件。我們使用 ildasm.exe 工具打開這個文件,定位到 Calculator 的 Add 方法,可以看到 Add 方法的 IL 代碼如下:

  .method public hidebysig

  instance int32 Add (

  int32 num1,

  int32 num2

  ) cil managed

  {

  // Code size 9 (0x9)

  .maxstack 2

  .locals init (

  [0] int32

  )

  IL_0000: nop

  IL_0001: ldarg.1

  IL_0002: ldarg.2

  IL_0003: add

  IL_0004: stloc.0

  IL_0005: br.s IL_0007

  IL_0007: ldloc.0

  IL_0008: ret

  }

  以我的安裝環境為例,你可以在這個位置找到 ildasm.exe 工具:C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\ildasm.exe。為了使用方便,你可以把該工具配置到 Visual Studio 的外部工具中。

  這就是 IL 代碼的樣子,如果使用 VB 或 F# 編寫相同的 Add 方法,它生成的 IL 代碼會是一樣的。關于 IL 代碼語法后面有機會再講,這里我們暫且不關心。

  由于程序集中的 IL 代碼不是平臺特定的指令,所以 IL 代碼必須在使用前調用 JIT 編譯器進行即時編譯,將其編譯成特定平臺(特定的操作系統和 CUP 架構,如 Linux x64)的本地代碼,才能在該平臺運行起來。

  .NET Core 運行時會在 JIT 編譯過程中針對特定平臺再次進行底層優化。比如將 IL 代碼編譯成特定于某平臺的本地代碼時,它會把平臺無關的代碼剔除。并且,它會以適合目標操作系統的方式將編譯好的本地代碼緩存在內存中,供以后使用,下次不需要重新編譯 IL 代碼。

  元數據

  除了 CIL 代碼外,.NET Core 程序集還包含完整、全面、細致的元數據,它描述了程序集中定義的每個類型(如類、結構、枚舉),以及每個類型的成員(如屬性、方法、事件),這些信息生成都由編譯器自動完成的。

  我們繼續使用 ildasm.exe 來看看 .NET Core 元數據具體長什么樣。以前面的代碼為例,選擇該程序集,依次點擊“視圖->元信息->顯示”,可以看到當前程序集的所有元數據信息。我們可以在元數據信息中找到 Calculator 類的 Add 方法,它的元數據是這樣的:

  TypeDef #2 (02000003)

  -------------------------------------------------------

  TypDefName: ConsoleApp.Calculator (02000003)

  Flags : [NotPublic] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit] (00100000)

  Extends : 0100000C [TypeRef] System.Object

  Method #1 (06000003)

  -------------------------------------------------------

  MethodName: Add (06000003)

  Flags : [Public] [HideBySig] [ReuseSlot] (00000086)

  RVA : 0x00002090

  ImplFlags : [IL] [Managed] (00000000)

  CallCnvntn: [DEFAULT]

  hasThis

  ReturnType: I4

  2 Arguments

  Argument #1: I4

  Argument #2: I4

  2 Parameters

  (1) ParamToken : (08000002) Name : num1 flags: [none] (00000000)

  (2) ParamToken : (08000003) Name : num2 flags: [none] (00000000)

  元數據會被 .NET Core 運行時以及各種開發工具所使用。例如,Visual Studio 等工具所提供的智能提示功能就是通過讀取程序集的元數據而實現的。元數據也被各種對象瀏覽工具、調試工具和 C# 編譯器本身所使用。元數據是眾多 .NET Core 技術的支柱,比如反射、對象序列化等。

  程序集清單

  .NET Core 程序集還包含描述程序集本身的元數據,我們稱之為清單。清單記錄了當前程序集正常運行所需的所有外部程序集、程序集的版本號、版權信息等等。與類型元數據一樣,生成程序集清單也是由編譯器的工作。

  同樣地,還是以上面 Calculator 類所在項目為例,我們也來看看程序集清單長什么樣子。在 ildasm.exe 工具打開的程序集的目錄樹中,雙擊 MAINFEST 即可查看程序集的清單內容:

  .assembly extern Systemtime

  {

  lickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:

  .ver 5:0:0:0

  }

  .assembly extern System.Console

  {

  lickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:

  .ver 5:0:0:0

  }

  .assembly ConsoleApp

  {

  ...

  .custom instance void ... TargetFrameworkAttribute ...

  .custom instance void ... AssemblyCompanyAttribute ...

  ...

  .hash algorithm 0x00008004

  .ver 1:0:0:0

  }

  .module ConsoleApp.dll

  agebase 0x00400000

  .file alignment 0x00000200

  .stackreserve 0x00100000

  .subsystem 0x0003 // WINDOWS_CUI

  .corflags 0x00000001 // ILONLY

  可以看到,程序集清單首先通過 .assembly extern 指令記錄了它所引用的外部程序集。接著是當前程序集本身的信息,記錄了程序集本身的各種特征,如版本號、模塊名稱等。

  提前編譯 IL 代碼

  前面提到,IL 代碼需要先通過 JIT 編譯器編譯成特定平臺的本地代碼,才能在該平臺運行。你可能會問,.NET 為什么要將源代碼編譯成 IL 代碼,而不直接編譯成特定平臺的本地代碼呢?

  這樣做主要有兩個好處:一是語言整合,一套運行時環境可以運行多種語言編寫的程序,.NET 團隊不用開發和維護多套運行時;二是平臺無關,方便程序和庫的移植,編譯后的程序集可以發布到多個平臺,而不用為不同的平臺發布特定的程序文件。雖然 IL 代碼帶來了可移植性等的好處,但需要以犧牲一點點啟動時的性能作為代價。

  一般我們的 Web 應用程序最終只會部署在一種平臺(如 Linux x64),為了更快的啟動性能,在啟動時,我們確實可以不需要中間語言編譯這個環節,省去啟動時的 JIT 編譯的時間。.NET Core 為我們提供了兩種方式把 IL 代碼提前編譯成特定平臺的本地代碼。

  一種方式是使用 ReadyToRun 功能。.NET Core 運行時(CoreCLR)中的一個叫做 CrossGen 的工具,它可以預先將 IL 代碼編譯成本地代碼。要使用這個功能,只需在程序發布的時候,選擇特定平臺,在發布選項中勾選 Enable ReadyToRun compilation 即可。不過 ReadyToRun 功能目前只適用于 Windows 系統。

  另一種方式是使用 .NET 5 新增加的 AOT 編譯功能。發布時選擇 Self-Contained 模式,發布后生成單個文件。AOT 編譯也是提前將 IL 代碼編譯成本地代碼,不同的是,它在發布時生成的單個文件還包含一個精簡版的本地運行時。這點在第 5 篇文章講過,不再累述了。

  這兩種方式都有弊端,第一種目前只適用于 Windows 系統,第二種 Self-Contained 單個文件發布要比多文件發布大幾十 M。不過對于第一次啟動慢那么一點點(可能甚至不到一秒的時間),大部分的 Web 應用程序都是完全可以接受的。如果實在對啟動時性能有嚴格的要求,也可以使用預熱的方案。

  小結

  本文介紹了程序集以及它的內部組成:IL 代碼、元數據、資源文件和程序集清單。總的來說,程序集就是 .NET Core 在編譯后生成的 *.dll 文件,它包含托管模塊、資源文件和程序集清單,其中托管模塊由 IL 代碼和元數據組成。

  需要強調的是,IL 代碼不包含特定平臺的指令,它只在需要的時候才會被 CoreCLR 運行時中的 JIT 編譯器編譯成特定于平臺的本地代碼。

  通過本文,相信大家對 .NET Core 程序集和它的內部組成已經有了一個整體的認識。

?

總結

以上是生活随笔為你收集整理的.NET 之路 | 007 详解 .NET 程序集的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。