原文地址: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