开源电子书《WoW C#》

开源电子书《WoW C#》

.NET CLI简单教程和项目结构

 

本文内容来自我写的开源电子书《WoW C#》,现在正在编写中,可以去WOW-Csharp/学习路径总结.md at master · sogeisetsu/WOW-Csharp (github.com)来查看编写进度。预计2021年年底会完成编写,2022年2月之前会完成所有的校对和转制电子书工作,争取能够在2022年将此书上架亚马逊。编写此书的目的是因为目前.NET市场相对低迷,很多优秀的书都是基于.NET framework框架编写的,与现在的.NET 6相差太大,正规的.NET 5学习教程现在几乎只有MSDN,可是MSDN虽然准确优美但是太过琐碎,没有过阅读开发文档的同学容易一头雾水,于是,我就编写了基于.NET 5的《WoW C#》。本人水平有限,欢迎大家去本书的开源仓库sogeisetsu/WOW-Csharp关注、批评、建议和指导。

WHAT IS .NET CLI ?

.NET 命令行接口 (CLI) 工具是用于开发、生成、运行和发布 .NET 应用程序的跨平台工具链。


来源:.NET CLI | Microsoft Docs

.NET CLI是.NET官方的一个命令行工具。本文将介绍.NET CLI的几个主要的命令。并通过这几个命令来了解.NET控制台程序的项目结构。

不建议在学习阶段使用IDE

IDE是一种非常好的编程工具,但是,在初学阶段,并不建议使用IDE,因为IDE会让学习的人丧失对程序第一手的感知,有相当多的人在使用高级的框架开发的时候会出现没有IDE就不会开发的现象,我对IDE的看法是:“开发需要依赖IDE,但是不能依靠IDE”,也就是说,在没有IDE的情况下要依然能够会编写程序,会运行和发布程序。

没有IDE,那么就需要用.NET CLI创建、运行、构建和发布程序,用文本编写软件编写程序。文本编写软件可以用系统自带的记事本,但是更建议使用带“代码高亮和方法跳转”的文本编写软件,比如github开发的Atom或者微软的vscode

微软的Visual Studio 2019是一个比较好的.NET开发IDE。它的用法将在后面讲到。

事实上,强大的IDE(Visual Studio)在进行构建、编译和发布程序等操作的时候,依靠的也是.NET CLI

下载并安装.NET CLI

.NET CLI.NET SDK的一部分,下载.NET CLI的前提是去下载.NET SDK.NET SDK 是一组用于开发和运行 .NET 应用程序的库和工具。我们一般意义上讲的.NET就是指的.NET SDK

.NET 命令行接口 (CLI) 工具是用于开发、生成、运行和发布 .NET 应用程序的跨平台工具链。

.NET CLI 附带了 .NET SDK。 若要了解如何安装 .NET SDK,请参阅安装 .NET Core


来源:.NET CLI | Microsoft Docs

如果在CMD输入dotnet --version之后,有正确的关于dotnet版本的回应的话,那么就证明.NET SDK已经安装成功,作为.NET SDK一部分的.NET CLI也已经安装完毕。

(base) PS C:\Users\苏月晟\Desktop> dotnet --version
5.0.401

安装文本编辑器

前面已经讲了不建议在学习阶段使用高级的IDE,所以推荐一个好用的文本编辑器——Visual Studio Code。简称“vscode”。

vscode是微软的一款开源产品,它带有代码高亮功能,支持丰富的插件,可以直接调用终端,也可以直接在终端里面通过code命令直接调用vscode。更重要的是微软官方文档里面将.NET CLI的实例讲解和vscode的使用放在了一起。

下载安装完成之后,安装三个插件,安装方式是点击左侧的插件按钮,进行搜索安装。

开源电子书《WoW C#》

三个插件如下:

  • Chinese (Simplified) (简体中文) Language Pack for Visual Studio Code
  • C#
  • C# XML Documentation Comments

这三个插件,一个是将vscode界面翻译成中文,一个是为编写和调试C#代码提供支持,最后一个插件是可以自动生成类注释和方法注释的模板。

.NET CLI 初级命令

初级命令就是newrunbuildpublish

dotnet new

根据指定的模板,创建新的项目、配置文件或解决方案。

dotnet new <TEMPLATE> [--dry-run] [--force] [-lang|--language {"C#"|"F#"|VB}]
    [-n|--name <OUTPUT_NAME>] [-o|--output <OUTPUT_DIRECTORY>] [Template options]

dotnet new -h|--help

常见的命令是dotnet new console -o <appname>

console是模板的名称,代表的是控制台程序。可以运行 dotnet new --list 以查看所有已安装模板的列表。

常用命令选项

  • -o|--output <OUTPUT_DIRECTORY>

    指定的是项目的输出目录,同时也代表了项目的名称。

  • -n|--name <OUTPUT_NAME>

    指定所创建的输出的名称。 如果未指定名称,使用的是当前目录的名称。

  • -lang|--language {C#|F#|VB}

    一般情况下默认的语言就是C#

项目结构

假设使用dotnet new console -o MyAppOne创建了一个名为MyAppOne的控制台应用,那么在没有经过编译运行的情况下MyAppOne文件夹下面的结构应该是这样的:

.
├── MyAppOne.csproj
├── Program.cs
└── obj

每个文件的作用

  • MyAppOne.csproj

    这个文件是项目组织和配置的核心,会在生成时被MSBuild用来作为输入

  • **obj **

    文件夹包含了编译与生成时需要的和产生的中间文件,一般不需要关注。

  • Progam.cs

    就是程序的代码文件,里面已经写了一段最基本的HelloWord代码。

csproj 文件的本质是一个保存项目信息的xml文件。默认配置如下:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

</Project>

项目系统和 MSBuild

.NET 应用是使用 MSBuild 从源代码中生成的。 项目文件(.csproj、.fsproj 或 .vbproj)指定目标和负责编译、打包和发布代码的关联任务 。 有引用目标和任务的标准集合的 SDK 标识符。 使用这些标识符有助于使项目文件较小且易于使用。

关于MSBuild的常用属性和命令行将在后面讲到。

dotnet run

无需任何显式编译或启动命令即可运行源代码。

dotnet run [-a|--arch <ARCHITECTURE>] [-c|--configuration <CONFIGURATION>]
    [-f|--framework <FRAMEWORK>] [--force] [--interactive]
    [--launch-profile <NAME>] [--no-build]
    [--no-dependencies] [--no-launch-profile] [--no-restore]
    [--os <OS>] [--project <PATH>] [-r|--runtime <RUNTIME_IDENTIFIER>]
    [-v|--verbosity <LEVEL>] [[--] [application arguments]]

dotnet run -h|--help

dotnet run的本质其实上是编译并运行。编译这一步它其实是相当于使用dotnet build,运行这一步相当于运行依赖于框架的应用程序 DLL,比如dotnet MyAppOne.dll

命令取决于生成代码的 dotnet build 命令。 对于此生成的任何要求,例如项目必须首先还原,同样适用于 dotnet run


来源:dotnet run 命令 - .NET CLI | Microsoft Docs

输出文件会写入到默认位置,即 bin/<configuration>/<target>。 例如,如果具有 netcoreapp2.1 应用程序并且运行 dotnet run,则输出置于 bin/Debug/netcoreapp2.1。 将根据需要覆盖文件。 临时文件将置于 obj 目录。

常用命令选项

  • -c|--configuration <CONFIGURATION>

    定义生成配置。 大多数项目的默认配置为 Debug,但你可以覆盖项目中的生成配置设置。也就是说configuration默认是Debug,运行dotnet run之后的输出文件会在bin/Debug之下。

  • -f|--framework <FRAMEWORK>

    使用指定框架生成并运行应用。 框架必须在项目文件中进行指定。一般情况下不需要在命令行中指定框架,只要在MSBuild文件(.csproj)文件里面指定一个<TargetFramework>就可以了,如果该项目指定多个框架,在不使用 -f|--framework <FRAMEWORK> 选项指定框架时,执行 dotnet run 将导致错误。也就是说,一般情况下,默认的框架是生成项目的.NET SDK的版本,我安装的是,NET 5.0,那么我又没有更改<TargetFramework>,那么运行dotnet run之后的输出文件会在bin/Debug/net5.0之下。

一般情况下,是在项目根目录运行dotnet run

项目结构

尝试对在dotnet new一节中生成的控制台项目MyAppOne的根目录运行dotnet run。会运行默认的Program.cs,输出Hello World!

项目结构会增加一个bin目录:

.
├── MyAppOne.csproj
├── Program.cs
├── bin
└── obj

bin目录的结构如下:

.
└── Debug
    └── net5.0
        ├── MyAppOne.deps.json
        ├── MyAppOne.dll
        ├── MyAppOne.exe
        ├── MyAppOne.pdb
        ├── MyAppOne.runtimeconfig.dev.json
        ├── MyAppOne.runtimeconfig.json
        └── ref
            └── MyAppOne.dll

为什么执行dotnet run之后的输出文件会在bin/Debug/net 5.0之下,已经在前面dotnet run的选项里讲的很清楚了,就不再赘述。下面讲一下输出文件的每一个文件的作用。

每个文件的作用

  • MyAppOne.deps.json

    保存一个依赖的列表,也就是编译上下文数据和编译依赖,不是技术上需要的,但是在使用服务,包缓存或共享这些安装功能的时候是需要的。这个文件由程序进行处理和使用,用户不应编辑。

  • MyAppOne.dll

    托管程序集,包括符合 ECMA 的入口点令牌,通俗说就是程序本体。运行它需要dotnet MyAppOne.dll

  • MyAppOne.exe

    可执行文件,双击就能运行。这个东西应该不用多解释了,使用过windows的人应该对exe不陌生。

  • MyAppOne.runtimeconfig.json

    包含运行时配置设置的可选配置文件。这个可选可不是说这个文件可有可无,它很重要,甚至缺少他之后甚至无法运行DLL程序,这个可选指的是再这个文件里面可以对相关配置进行选择。

  • MyAppOne.pdb

    该文件记录了代码中断点等调试信息,没有它,打的断点就不好使了,在Visual Studio 2019中有一个Debug模式,调试代码时使用Debug模式,打的断点才能完全发挥作用,否则就只能是告诉开发者出什么错了+错误在哪行。在vscode里面调试默认是的就是这个模式。这个文件不应该由用户编辑。

  • MyAppOne.runtimeconfig.dev.json

    一个可有可无的文件,他的作用是增加附加的运行时探索路径,dotnet的GitHub的讨论上可以看到dotnet官方已经着手取消这个文件(参见Reason for runtimeconfig.dev.json in build output since .NET Core 3? )。因为大于等于.NET Core 3.0的版本,会将 NuGet 中的库依赖项复制到输出文件夹, 而不是在运行时从 NuGet 全局包文件夹中对其进行解析。也就是说在.NET 5.0这个版本中,runtimeconfig.dev.json这个文件基本没有意义。删除这个文件不会影响运行。

  • ref文件夹

    引用程序集 是一种特殊类型的程序集,仅包含程序集的公共接口的程序集,它们有助于加快构建过程,因为依赖于该程序集的项目将能够看到没有理由重新编译,即使组件的内部发生了变化,因为从外表上看,它仍然是一样的。如果想取消ref目录的生成,在MSBuild文件中添加<ProduceReferenceAssembly>false</ProduceReferenceAssembly>即可。

dotnet build

生成项目及其所有依赖项,可以类比为编译。

dotnet build [<PROJECT>|<SOLUTION>] [-a|--arch <ARCHITECTURE>]
    [-c|--configuration <CONFIGURATION>] [-f|--framework <FRAMEWORK>]
    [--force] [--interactive] [--no-dependencies] [--no-incremental]
    [--no-restore] [--nologo] [--no-self-contained] [--os <OS>]
    [-o|--output <OUTPUT_DIRECTORY>] [-r|--runtime <RUNTIME_IDENTIFIER>]
    [--self-contained [true|false]] [--source <SOURCE>]
    [-v|--verbosity <LEVEL>] [--version-suffix <VERSION_SUFFIX>]

dotnet build -h|--help

输出文件会写入到默认位置,即 bin/<configuration>/<target>

常用命令选项

  • -c|--configuration <CONFIGURATION>

    定义生成配置。 大多数项目的默认配置为 Debug,但你可以覆盖项目中的生成配置设置。也就是说configuration默认是Debug,运行dotnet build之后的输出文件会在bin/Debug之下。

  • -f|--framework <FRAMEWORK>

    编译特定框架。 必须在项目文件中定义该框架。一般情况下不需要在命令行中指定框架,只要在MSBuild文件(.csproj)文件里面指定一个<TargetFramework>就可以了,如果该项目指定多个框架,在不使用 -f|--framework <FRAMEWORK> 选项指定框架时,执行 dotnet run 将导致错误。也就是说,一般情况下,默认的框架是生成项目的.NET SDK的版本,我安装的是,NET 5.0,那么我又没有更改<TargetFramework>,那么运行dotnet build之后的输出文件会在bin/Debug/net5.0之下。

  • -r|--runtime <RUNTIME_IDENTIFIER>

    指定目标运行时。 有关运行时标识符 (RID) 的列表,请参阅 RID 目录。 如果将此选项与 .NET 6.0 SDK 结合使用,则还要使用 --self-contained 或 --no-self-contained。指定了目标运行时之后,.NET 运行时随应用程序一同发布。文件输出在 bin/<configuration>/<target>/RID之中。常见的RID有win-x64win-x86Linux-x64

Debug vs Release

dotnet rundotnet builddotnet publish中都有一个选项,叫做-c|--configuration <CONFIGURATION>。微软官方对这个选项的解释是:

定义生成配置。 大多数项目的默认配置为 Debug,但你可以覆盖项目中的生成配置设置。

显然,微软的官方解释并不容易理解。其实Debug和Release两种模式是.NET默认的两种模式,默认情况下,Debug模式生成的pdb文件含有全部的断点信息,在进行调试的时候,保存着调试和项目状态信息、有断言、堆栈检查等代码。在Visual Studio里面只有选择Debug模式才会有完整的调试信息,如果选择Release模式进行调试,基本上只能输出出什么错了+错误在哪行当然这只是.NET的默认设置,可以在Visual Studio里面进行自定义的配置。在Visual Studio Code里面,调试默认使用Debug模式。

Release模式并非一无是处,它编译时对应用程序的速度进行优化,使得程序在代码大小和运行速度上都是最优的

下面提供一些互联网上关于这两个模式的讨论:

The Release mode enables optimizations and generates without any debug data, so it is fully optimized. . Lots of your code could be completely removed or rewritten in Release mode. The resulting executable will most likely not match up with your written code. Because of this release mode will run faster than debug mode due to the optimizations.


来源:Difference between a Debug and Release build (net-informations.com)

The most important thing is that in Debug mode there are no optimizations, while in Release mode there are optimizations. This is important because the compiler is very advanced and can do some pretty tricky low-level improving of your code. As a result some lines of your code might get left without any instructions at all, or some might get all mixed up. Step-by-step debugging would be impossible. Also, local variables are often optimized in mysterious ways, so Watches and QuickWatches often don't work because the variable is "optimized away". And there are multitudes of other optimizations too. Try debugging optimized .NET code sometime and you'll see.

Another key difference is that because of this the default Release settings don't bother with generating extensive debug symbol information. That's the .PDB file you might have noticed and it allows the debugger to figure out which assembly instructions corresspond to which line of code, etc.


来源:.net - What is the difference between Debug and Release in Visual Studio? - Stack Overflow

建议按照这两个模式的字面意思来选择,比如说在调试阶段就用Debug模式,调试完毕之后的运行、构建和发布就用Release模式。这样既能保证在调试时能够看到最完整的调试信息,也能保证代码运行、构建和发布速度更快。

项目结构

和运行dotnet run之后的项目结构一致,参见dotnet run项目结构

想要保证编译后的文件能够运行MyAppOne.dllMyAppOne.deps.jsonMyAppOne.runtimeconfig.json不可或缺的。如果觉得build之后生成的文件太多,可以尝试使用.NET 6.0然后参考单文件应用程序 - .NET | Microsoft Docs,但是不建议在build阶段去尝试生成单文件,因为build的本质就是去生成项目及其所有依赖项。如果想要生成单文件,可以在部署阶段(publish)来尝试单文件部署。

dotnet publish

将应用程序及其依赖项发布到文件夹以部署到托管系统。

dotnet publish [<PROJECT>|<SOLUTION>] [-a|--arch <ARCHITECTURE>]
    [-c|--configuration <CONFIGURATION>]
    [-f|--framework <FRAMEWORK>] [--force] [--interactive]
    [--manifest <PATH_TO_MANIFEST_FILE>] [--no-build] [--no-dependencies]
    [--no-restore] [--nologo] [-o|--output <OUTPUT_DIRECTORY>]
    [--os <OS>] [-r|--runtime <RUNTIME_IDENTIFIER>]
    [--self-contained [true|false]]
    [--no-self-contained] [-v|--verbosity <LEVEL>]
    [--version-suffix <VERSION_SUFFIX>]

dotnet publish -h|--help

输出文件默认位置是 bin/<configuration>/<target>/publish

常用命令选项

基本上和dotnet build 的常用命令选项一致,有个比较大的不同就是使用-r|--runtime <RUNTIME_IDENTIFIER>时可以指定self-contained,不需要像dotnet build一样得等到.NET 6.0才能指定self-contained


来源:dotnet publish 命令 - .NET CLI | Microsoft Docs

项目结构

输出文件默认位置是 bin/<configuration>/<target>/publish

默认状态下项目结构和dotnet build项目结构不一样的点有两个,一个是没有了ref文件夹,一个是没有了runtimeconfig.dev.json

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2021/10/17      6:46            416 MyAppOne.deps.json
-a----        2021/10/15     16:01           4608 MyAppOne.dll
-a----        2021/10/15     16:01         125952 MyAppOne.exe
-a----        2021/10/15     16:01           9600 MyAppOne.pdb
-a----        2021/10/17      6:46            147 MyAppOne.runtimeconfig.json

单文件部署和可执行文件

单个文件部署与 Windows 7 不兼容。

有两个好方法:

  1. 修改MSBuild(.csrpoj)文件,然后dotnet publish

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net5.0</TargetFramework>
        <PublishSingleFile>true</PublishSingleFile>
        <SelfContained>true</SelfContained>
        <RuntimeIdentifier>win-x64</RuntimeIdentifier>
        <PublishReadyToRun>true</PublishReadyToRun>
      </PropertyGroup>
    
    </Project>
    
  2. 完全通过命令行,无需修改MSBuild(.csrpoj)文件

    dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained true
    

原理解释

首先,必须指定PublishSingleFile为真,这是生成单个文件的基础。SelfContained为真保证可以独立而不依赖框架,RuntimeIdentifier来指定系统和cpu类型,只有指定了这个,指定SelfContained才有意义

  • --self-contained [true|false]

    .NET 运行时随应用程序一同发布,因此无需在目标计算机上安装运行时。 如果指定了运行时标识符,并且项目是可执行项目(而不是库项目),则默认值为 true。 有关详细信息,请参阅 .NET 应用程序发布使用 .NET CLI 发布 .NET 应用

    如果在未指定 true 或 false 的情况下使用此选项,则默认值为 true。 在这种情况下,请不要紧接在 --self-contained 后放置解决方案或项目参数,因为该位置需要 true 或 false


来源:dotnet publish 命令 - .NET CLI | Microsoft Docs

即使是发布单文件,--self-contained也要根据需求慎重考虑。

dotnet restore

dotnet restore 命令使用 NuGet 还原依赖项以及在 project 文件中指定的特定于项目的工具。 在大多数情况下,不需要显式使用 dotnet restore 命令,因为在运行以下命令时,将会在必要时隐式运行 NuGet 还原:

build vs publish

最开始的时候,这俩的区别是输出的文件能否在另一台机器上运行(is ready to be transferred to another machine to run.)。但是随着.NET框架的发展,build和publish的含义也在发生着变化(其实这要怪微软,不知道从什么时候开始,感觉微软整个公司氛围开始变成说话不算数,朝令夕改了)。

本文无意去讨论在整个漫长的.NET发展周期( .NET Framework、 .NET Core等 )内这两个命令含义的变化,现在是2021年10月17日,目前最近的稳定版是.NET 5.0,所以本文只讨论.NET 5.0中build和publish的区别。

  • 对于面向 .NET Core 3.0 及更高版本的可执行项目,库依赖项会被复制到输出文件夹。 这意味着如果没有其他任何特定于发布的逻辑(例如,Web 项目具有的逻辑),则应可部署生成输出。也就是说.NET 5.0在没有特殊情况下,build和publish都做好了在另一台机器上的准备。
  • 相比较于运行dotnet build之后文件夹的变化,dotnet publish会在原有的基础上生成bin/<configuration>/<target>/publish文件夹,publish文件夹和bin/<configuration>/<target>文件夹相比没有了ref文件夹和runtimeconfig.dev.json文件。
  • dotnet publish适用于部署成单文件。
  • dotnet publish适用于定于生成的文件是否依赖框架(--self-contained [true|false])

MSBuild

.NET 应用是使用 MSBuild 从源代码中生成的。 项目文件(.csproj、.fsproj 或 .vbproj)指定目标和负责编译、打包和发布代码的关联任务 。 有引用目标和任务的标准集合的 SDK 标识符。 使用这些标识符有助于使项目文件较小且易于使用。

MSBuild进行修改有两个方法,一个是在文件中修改,一个是在命令行中修改。其中在命令行中修改适用于dotnet build -pdotnet publish -p,但不适用于dotnet run。也可以用MSBuild.exe [Switches] [ProjectFile]来进行命令行修改,可以参考MSBuild 命令行参考 - MSBuild | Microsoft Docsdotnet publish 命令 - .NET CLI | Microsoft Docs

dotnet builddotnet publish可以用-p:<NAME>=<VALUE>来定义MSBuild属性,常用的几个属性如下:

  • PublishSingleFile

    启用单文件发布。 此外,还会在 dotnet build 期间启用单一文件警告。

  • SelfContained

    确定应用是独立的还是依赖于框架的。

  • RuntimeIdentifier

    指定目标 OS 和 CPU 类型。 默认情况下,还会设置 <SelfContained>true</SelfContained>

  • PublishReadyToRun

    启用预先 (AOT) 编译

注意,.NET 5.0中dotnet build设置SelfContained无效。

关于MSBuild的深度中文解析可以看理解 C# 项目 csproj 文件格式的本质和编译流程 - walterlv

.NET CLI 发布 .NET 应用

.NET 提供了三种发布应用程序的方法。 依赖于框架的部署会生成一个跨平台 .dll 文件,此文件使用本地安装的 .NET 运行时。 依赖于框架的可执行文件会生成一个特定于平台的可执行文件,此文件使用本地安装的 .NET 运行时。 独立式可执行文件会生成一个特定于平台的可执行文件,并包含 .NET 运行时的本地副本。

发布模式 SDK 版本 命令
依赖框架的部署 2.1 dotnet publish -c Release
  3.1 dotnet publish -c Release -p:UseAppHost=false
  5.0 dotnet publish -c Release -p:UseAppHost=false
依赖于框架的可执行文件 3.1 dotnet publish -c Release -r <RID> --self-contained false dotnet publish -c Release
  5.0 dotnet publish -c Release -r <RID> --self-contained false dotnet publish -c Release
独立部署 2.1 dotnet publish -c Release -r <RID> --self-contained true
  3.1 dotnet publish -c Release -r <RID> --self-contained true
  5.0 dotnet publish -c Release -r <RID> --self-contained true

上述表格来源:使用 .NET CLI 发布应用 - .NET | Microsoft Docs

另外,还可以通过PublishSingleFile来部署成单个可执行文件

参考文档

LICENSE

copyright © 2021 苏月晟,版权所有。

上一篇:Python中openpyxl模块的一些用法


下一篇:专家警告:移动应用破坏Web开放计算基础