原文地址:https://natemcmaster.com/blog/2017/12/21/netcore-primitives/javascript
我學習過使用 gcc,C++ 和 vim 編程。當我開始使用 C# 和 .NET 的時候,點擊 Visual Studio 中的 運行
按鈕就是魔法,也帶者失望。失望 - 不是由於我但願編寫 Makefile - 而是由於我不知道 運行
都作了什麼。因此,我開始探索。在本博文中,我將展現在 .NET Core 中使用的多數基礎工具,並手工建立 .NET Core 應用程序而不借助於 Visual Studio。若是你是 .NET Core 的新手,而且但願揭開內幕,本文就是爲您而來。若是您已是一個 .NET Core 的開發者,而且好奇 *.deps.json 或者 *.runtimeconfig.json 文件是作什麼的,我也會涵蓋這些內容。html
我將會終止 Visual Studio 的魔法,而一直使用命令行工具。爲了你可以進行,您須要 java
.NET Core 2.1 SDK ( 實際上,.NET Core 3.1 SDK 已經發布,我想你更應該下載這個最新版)。下面的這些步驟是在 macOS 上完成的,可是它們也一樣在 Linux 和 Windows 上同樣工做,若是您將路徑更改成 c:\Program Files\dotnet\
和 dotnet.exe
的話。git
C# 編譯器將 *.cs 文件編譯爲 *.dll 文件,也被稱爲程序集文件。程序集文件具備便攜可執行文件格式,.NET Core 能夠在 Windows、macOS 和 Linux 上執行它。.NET Core app
是一系列 *.dll 文件的集合 (包括少許的配置文件)。它能夠經過多種程序設計語言,例如 VB 或者 F# 等所生成,可是,C# 是最經常使用的一種。github
C# 編譯器能夠直接調用來生成程序集文件。C# 編譯器能夠在 .NET Core SDK 中發現,並像下面這樣被調用。shell
dotnet /usr/local/share/dotnet/sdk/2.1.3/Roslyn/bincore/csc.dll -help
讓咱們爲它提供一個輸入內容。首先,建立名爲 Program.cs
的文件,並編寫以下 C# 代碼:編程
/* Program.cs */ class Program { static void Main(string[] args) => System.Console.WriteLine("Hello World!"); }
而後,在命令行,執行以下命令:json
> dotnet \ /usr/local/share/dotnet/sdk/2.1.3/Roslyn/bincore/csc.dll \ -reference:/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app/2.0.0/ref/netcoreapp2.0/System.Runtime.dll \ -reference:/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app/2.0.0/ref/netcoreapp2.0/System.Console.dll \ -out:Program.dll \ Program.cs
在 .NET Core 3.1 中,NuGetFallbackFoler
已經從 sdk
文件夾中移除了。這些程序集已經轉移到 C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1
文件夾中。vim
若是在 Windows 下,注意空格的處理:api
dotnet 'C:\Program Files\dotnet\sdk\3.1.100\Roslyn\bincore\csc.dll' -reference:'C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.Runtime.dll' -reference:'C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.Console.dll' -out:Program.dll Program.cs
參數的含義以下:
dotnet - C# 編譯器自己也是一個 .NET Core 應用程序,因此,咱們須要經過 dotnet 命令來啓動它
/usr/local/share/dotnet/sdk/2.1.3/Roslyn/bincore/csc.dll - C# 編譯器的路徑。在 Windows 上,路徑就是: C:\Program Files\dotnet\
-reference 參數指向了 System.Runtime.dll 和 System.Console.dll,這些相似於 C++ 中的頭文件,它們爲編譯器提供關於 System.Object
和 System.Console
的信息。
-out:Program.dll,輸出文件名。.dll
的擴展名是 .NET Core 的約定,並非必需的。若是沒有指定,編譯器將生成名爲 Program.exe
的文件。在 Windows 系統上,這會致使一點誤解,由於你並不能經過雙擊 Program.exe 來啓動它,因此,在 .NET Core 中,咱們老是使用 .dll 擴展名。
Reference 引用容許咱們使用代碼中涉及的在其它 .NET Core 代碼中定義的成千上萬的類型,例如 List
、Integer
以及 HttpClient
類型等等。可是,你不得不告訴編譯器到哪裏去找到它們。若是你刪除掉 -reference:***
部分,編譯器將會失敗,並返回以下錯誤:
Program.cs(1,11): error CS0518: Predefined type 'System.Object' is not defined or imported Program.cs(3,26): error CS0518: Predefined type 'System.String' is not defined or imported Program.cs(3,16): error CS0518: Predefined type 'System.Void' is not defined or imported
示例中使用的路徑是 /usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app
。這些來自與 Microsoft.NETCore.App
這個 NugGet 包,後面咱們會討論它。
對於 .NET Core 應用程序來講,runtime.config.json
文件是必需的。術語 runtime
、shared framework
、或者 platform
常常互換,可是,在談論 .NET Core 的時候,它們是一回事。該 JSON 配置文件用於運行時。
若是您擁有了上一步所獲得的程序集,您能夠試着在命令行運行它,經過 dotnet
工具。沒有這個 runtime.config.json
,該嘗試將會失敗:
>dotnet Program.dll A fatal error was encountered. The library 'libhostpolicy.dylib' required to execute the application was not found in '/Users/nmcmaster/code/'.
在 Windows 環境的 .NET Core 3.1 環境下,我獲得是:
>dotnet Program.dll A fatal error was encountered. The library 'hostpolicy.dll' required to execute the application was not found in 'C:\temp\dotnet\'. Failed to run as a self-contained app. If this should be a framework-dependent app, add the C:\temp\dotnet\Program.runtimeconfig.json file specifying the appropriate framework.
該段說明的意思是,.NET Core 不能找到用於執行 Program.dll 文件所必需的某些文件。爲了解決這個問題,建立名爲 Program.runtimeconfig.json
的文件,並使用以下內容:
{ "runtimeOptions": { "framework": { "name": "Microsoft.NETCore.App", "version": "2.0.0" } } }
注意,在 .NET Core .3.1 下,文件內容以下:
{ "runtimeOptions": { "framework": { "name": "Microsoft.NETCore.App", "version": "3.1.0" } } }
這些設置指示 dotnet
使用 Microsoft.NETCore.App 3.1.0
共享框架。該框架也是最常使用的框架,可是,還有其它的框架,例如 Microsoft.AspNetCore.App
。不像 .NET Framework 是裝個機器範圍生效,能夠有多個 .NET Core 共享框架安裝在同一臺機器上。dotnet
將讀取該 JSON 文件,並在 /usr/local/share/dotnet/shared/$FrameworkName/$Version/
中查找須要的文件並運行應用程序。
說明:若是有更高版本的
Microsoft.NeTCore.App
補丁安裝,例如shared/Microsoft.NETCore.App/2.0.4/
,dotnet
將自動使用更高版本。
如今,執行 dotnet Program.dll
。
>dotnet Program.dll Hello world!
包提供了在不一樣項目之間、項目組之間以及組織之間共享代碼的方式,.NET 程序集被打包到 *.nupkg
文件中,這僅僅是一個 ZIP 壓縮格式文件,並含有一個 XML 文件 (.nuspec) ,包含有關於該包的元數據。
最流行的一個 .NET 包稱爲 JSON.NET,也被稱爲 Newtonsoft.Json。它提供瞭解析和序列化 JSON 的 API。咱們能夠從 NuGet.org 獲得它並提取到磁盤上。
# Bash mkdir -p ./packages/Newtonsoft.Json/10.0.3/ curl -L https://www.nuget.org/api/v2/package/Newtonsoft.Json/10.0.3 | tar -xf - -C ./packages/Newtonsoft.Json/10.0.3/
Windows 環境下
# Windows (powershell) mkdir ./packages/Newtonsoft.Json/10.0.3/ Invoke-WebRequest https://www.nuget.org/api/v2/package/Newtonsoft.Json/10.0.3 -OutFile Newtonsoft.Json.10.0.3.zip Expand-Archive Newtonsoft.Json.10.0.3.zip -D ./packages/Newtonsoft.Json/10.0.3/
爲了演示它的使用,咱們將更新前一步的示例代碼,以 JSON 對象格式輸出信息。
class Program { static void Main(string[] args) => System.Console.WriteLine( Newtonsoft.Json.JsonConvert.SerializeObject(new { greeting = "Hello World!" })); }
這須要添加更多的編譯參數到編譯器的參數列表中,以便使用 Newtonsoft.Json 的 API。
> dotnet /usr/local/share/dotnet/sdk/2.1.3/Roslyn/bincore/csc.dll \ -reference:/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app/2.0.0/ref/netcoreapp2.0/System.Runtime.dll \ -reference:/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app/2.0.0/ref/netcoreapp2.0/System.Console.dll \ -reference:/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app/2.0.0/ref/netcoreapp2.0/System.Collections.dll \ -reference:./packages/Newtonsoft.Json/10.0.3/lib/netstandard1.3/Newtonsoft.Json.dll \ -out:Program.dll \ Program.cs
使用 .NET Core 3.1 的命令以下:
dotnet 'C:\Program Files\dotnet\sdk\3.1.100\Roslyn\bincore\csc.dll' -reference:'C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.Runtime.dll' -reference:'C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.Console.dll' -reference:'C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.Collections.dll' -reference:'./packages/Newtonsoft.Json/10.0.3/lib/netstandard1.3/Newtonsoft.Json.dll' -out:Program.dll Program.cs
注意:顯然咱們須要
reference:Newtonsoft.Json.dll
,可是,爲何須要System.Collections.dll
?這是由於咱們還使用了匿名類型,new { greeting }
。在背後,C# 編譯器在匿名類型上生成了一個.Equals()
方法,該方法調用了System.Collections.Generic.EqualityComparer
,它定義在System.Collections.dll
中。
編譯應當成功,雖然帶有一些警告信息。
Program.cs(4,35): warning CS1701: Assuming assembly reference 'System.Runtime, Version=4.0.20.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' used by 'Newtonsoft.Json' matches
identity 'System.Runtime, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
of 'System.Runtime', you may need to supply runtime policy
在 .NET Core 3.1 下,實際上,我獲得的輸出信息以下:
Microsoft (R) Visual C# Compiler version 3.4.0-beta4-19562-05 (ff930dec)
Copyright (C) Microsoft Corporation. All rights reserved.
warning CS1701: Assuming assembly reference 'System.Runtime, Version=4.0.20.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' used by 'Newtonsoft.Json' matches identity 'System.Runtime, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' of 'System.Runtime', you may need to supply runtime policy
Program.cs(5,11): warning CS1701: Assuming assembly reference 'System.Runtime, Version=4.0.20.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' used by 'Newtonsoft.Json' matches identity 'System.Runtime, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' of 'System.Runtime', you may need to supply runtime policy
這意味着在 Newtonsoft.Json
的做者建立 Newtonsoft.Json.dll
的時候,他基於的 System.Runtime.dll
的版本是 4.0.20.0
。可是,如今提供的 System.Runtime.dll
更新一些,版本是 4.2.0.0
。若是在版本 4.0.20.0
到 4.2.0.0
之間有變化的化,會致使你運行的應用程序出現問題,因此,編譯器發出警告。幸運的是,這些變動是後向兼容的,因此 Newtonsoft.Json
將工做正常。咱們能夠經過添加參數 -nowarn:/CS1701
來抑制這些警告。
> dotnet /usr/local/share/dotnet/sdk/2.1.3/Roslyn/bincore/csc.dll \ -reference:/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app/2.0.0/ref/netcoreapp2.0/System.Runtime.dll \ -reference:/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app/2.0.0/ref/netcoreapp2.0/System.Console.dll \ -reference:/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app/2.0.0/ref/netcoreapp2.0/System.Collections.dll \ -reference:./packages/Newtonsoft.Json/10.0.3/lib/netstandard1.3/Newtonsoft.Json.dll \ -nowarn:CS1701 \ -out:Program.dll \ Program.cs
對於 Windows 環境下的 .NET Core 3.1,命令以下:
dotnet 'C:\Program Files\dotnet\sdk\3.1.100\Roslyn\bincore\csc.dll' -reference:'C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.Runtime.dll' -reference:'C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.Console.dll' -reference:'C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.Collections.dll' -reference:'./packages/Newtonsoft.Json/10.0.3/lib/netstandard1.3/Newtonsoft.Json.dll' -nowarn:CS1701 -out:Program.dll Program.cs
注意
CS1701
中的字母是大寫。
在上一步,咱們編譯了一個引用 Newtonsoft.dll
、System.Runtime.dll
和其它程序集的簡單應用程序。在添加 Newtonsoft.dll
以前,咱們的應用程序工做良好。可是,在更新版本以後,該程序的運行將會失敗。
> dotnet Program.dll Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'. The system cannot find the file specified.
.NET 在運行時動態連接程序集。編譯器在程序集 Program.dll
中添加了到 Newtonsoft.Json.dll
的引用,但並不會將代碼複製進來。.NET Core 運行時期待可以在應用程序執行的時候可以加載名爲 Newtonsoft.Json.dll
的程序集。對於 System.Runtime.dll
和 System.Console.dll
,以及其它引用的 System.* 文件也是一樣的。
.NET Core 能夠經過配置在一系列位置尋找 Newtonsoft.Json.dll
,可是,爲了簡單起見,咱們能夠將它複製到 Program.dll
的同一個文件夾中。
> cp ./packages/Newtonsoft.Json/10.0.3/lib/netstandard1.3/Newtonsoft.Json.dll ./ > dotnet Program.dll {"greeting":"Hello World!"}
爲何咱們不須要將 System.Runtime.dll
和其它文件複製過來呢?這些文件經過 Microsoft.NETCore.App 共享框架動態連接過來,如前面所述。
deps.json
文件是依賴說明文件。它能夠用來配置來自包的動態連接到程序集。如前所述,.NET Core 能夠配置爲從多個位置來動態加載程序集。這些位置包括:
應用程序所在的目錄,與應用程序入口相同的文件夾,不須要配置。
包的緩存文件夾 (NuGet 恢復緩存或者 NuGet 回落文件夾)
優化以後的包緩存,或者運行時包存儲。
服務目錄 (servicing index),不多使用,用於 Windows Update 方式
共享框架 (經過 runtimeconfig.json 配置)
綜上所述,deps.json
定義能夠動態連接的依賴列表,一般,該文件由機器生成,對於實際的應用程序,可能變得很大而且很複雜。可是,它是純文本形式的,因此咱們可使用編輯器來處理它。
添加名爲 Program.deps.json
的文件到項目中,內容以下:
{ "runtimeTarget": { "name": ".NETCoreApp,Version=v2.0" }, "targets": { ".NETCoreApp,Version=v2.0": { "Newtonsoft.Json/10.0.3": { "runtime": { "lib/netstandard1.3/Newtonsoft.Json.dll": { "assemblyVersion": "10.0.0.0", "fileVersion": "10.0.3.21018" } } } } }, "libraries": { "Newtonsoft.Json/10.0.3": { "type": "package", "serviceable": false, "sha512": "" } } }
爲了展現這是如何工做的,將如今的與 Program.dll
同一文件夾的 Newtonsoft.Json.dll
刪除。而後,執行 dotnet Program.dll
。
> rm Newtonsoft.Json.dll > dotnet Program.dll Error: An assembly specified in the application dependencies manifest (Program.deps.json) was not found: package: 'Newtonsoft.Json', version: '10.0.3' path: 'lib/netstandard1.3/Newtonsoft.Json.dll'
雖然提供了 Program.deps.json
文件,.NET Core 還須要一點關於到哪裏定位匹配 deps.json
文件中程序集的信息。該配置能夠經過以下三種方式之一實現:
*.runtimeconfig.dev.json
。這是配置的典型的最佳方式。添加文件 Program.runtimeconfig.dev.json
文件,其中設置了包文件夾的位置。它相似於 Program.runtimeconfig.json
文件,但它是可選的。典型地其中包含了文件徹底路徑,因此不適於在不一樣機器上發佈。
{ "runtimeOptions": { "additionalProbingPaths": [ "/Users/nmcmaster/code/packages/" ] } }
命令行。你可使用 exec
命令來手工指定 dotnet
命令中程序集的位置。使用 --additionalprobingppath
參數,能夠指定多個值。
> dotnet exec --additionalprobingpath ./packages/ Program.dll
*.runtimeconfig.json
。能夠添加一個運行時設置來指定新的探測位置。它可使用相對路徑。
{ "runtimeOptions": { "framework": { "name": "Microsoft.NETCore.App", "version": "2.0.0" }, "additionalProbingPaths": [ "./packages/" ] } }
注意:在個人 .NET Core 3.1 環境下,這個
additionalProbingPaths
沒有工做。這裏是 StackOverflow 上的一個問題:https://stackoverflow.com/questions/56844233/additional-probing-paths-for-net-core-3-migration
對於大多數的開發工做,並不須要這些基礎。相似 NuGet、MSBuild 和 Visual Studio 自動處理獲取應用,C# 文件,調用編譯器,連結到調試器,以及其它任務。可是,我認爲知道背後是如何工做的仍是很是用用。固然了,你還能夠更加深刻。在 *.dll 文件中實際有什麼?什麼是 *.pdb 文件?什麼是 crossgen 和 libcoreclr?我會把這些留在其它部分中。
Specs on runtimeconfig.json and deps.json: https://github.com/dotnet/cli/blob/v2.0.0/Documentation/specs/runtime-configuration-file.md
Assembly resolution and dynamic linking: https://github.com/dotnet/cli/blob/v2.0.0/Documentation/specs/corehost.md
原文出處:https://www.cnblogs.com/haogj/p/12003854.html