1.1 Hello, World
学习新语言最好的办法就是写代码。第一个例子是经典HelloWorld程序,它在屏幕上显示一些文本。代码清单1.1展示了完整HelloWorld程序,我们将在之后的小节编译并运行代码。
代码清单1.1 用C#编写的HelloWorld[1]
注意 C#是区分大小写的语言,大小写不正确会使代码无法成功编译。
有Java、C或者C++编程经验的读者很快就能看出相似的地方。类似于Java,C#也从C和C++继承了基本的语法[2]。语法标点(比如分号和大括号)、特性(比如区分大小写)和关键字(比如class、public和void)对于这些程序员来说并不陌生。初学者和其他语言背景的程序员通过这个程序能很快体会到这些构造的直观性。
1.1.1 创建、编辑、编译和运行C#源代码
写好C#代码后需要编译和运行。这时要选择使用哪个.NET实现(或者说.NET框架)。这些实现通常打包成一个软件开发包(Software Development Kit,SDK),其中包括编译器、运行时执行引擎、“运行时”能访问的语言可访问功能框架(参见1.7.1节),以及可能和SDK捆绑的其他工具(比如供自动化生成的生成引擎)。由于C#自2000年便已公布,目前有多个不同的.NET框架供选择(参见1.7节)。
取决于开发的目标操作系统以及你选择的.NET框架,每种.NET框架的安装过程都有所区别。有鉴于此,建议访问https://dotnet.microsoft.com/download了解具体的下载和安装指示。如有必要,先选好.NET框架,再根据目标操作系统选择要下载的包。虽然我可以在这里提供更多细节,但.NET下载站点为支持的各种组合提供了最新、最全的指令。
如不确定要使用的.NET框架,就默认选择.NET Core。它可运行于Linux、macOS和Microsoft Windows,是.NET开发团队投入最大的实现。另外,由于它具有跨平台能力,所以本书优先使用.NET Core。
有许多源代码编辑工具可供选择,包括最基本的Windows记事本、Mac/macOS TextEdit和Linux vi。但建议选择一个稍微高级点的工具,至少应支持彩色标注。支持C#的任何代码编辑器都可以。如果还没有特别喜欢的,推荐开源编辑器Visual Studio Code(https://code.visualstudio.com)。为了让Visual Studio Code更好地支持C#程序开发,请参考图1.1下载安装C#扩展模块。如果在Windows或Mac上工作,也可考虑Microsoft Visual Studio 2019(或更高版本),详情参考https://visualstudio.microsoft.com/vs/。两者都是免费的。
图1.1 为Visual Studio Code下载安装C#扩展模块
后两节我会提供这两种编辑器的操作指示。Visual Studio Code依赖命令行接口(CLI)工具dotnet CLI创建初始的C#程序基架并编译和运行程序。Windows和Mac则一般使用Visual Studio 2019。
使用dotnet CLI
dotnet命令dotnet.exe是dotnet命令行接口(或称dotnet CLI),可用于生成C#程序的初始代码库并编译和运行程序[3]。注意这里的CLI代表“命令行接口”(Command-Line Interface)。为避免和代表“公共语言基础结构”(Common Language Infrastructure)的CLI混淆,本书在提到dotnet CLI时都会附加dotnet前缀。无dotnet前缀的CLI才是“公共语言基础结构”。安装好之后,验证可以在命令行上执行dotnet。
以下是在Windows、macOS或Linux上创建、编译和执行HelloWorld程序的指示:
1.在Microsoft Windows上打开命令提示符,在Mac/macOS上打开Terminal应用。(也可考虑使用跨平台命令行接口PowerShell[4]。)
2.在想要放代码的地方新建一个目录。考虑./HelloWorld或./EssentialCSharp/HelloWorld这样的名称。在命令行上执行:
3.导航到新目录,使之成为命令行的当前目录:
4.在HelloWorld目录中执行dotnet new console命令来生成程序基架(或称程序项目)。这会生成几个文件,最主要的是Program.cs和项目文件:
5.运行生成的程序。这会编译并运行由dotnet new console命令创建的默认Program.cs程序。程序内容和代码清单1.1相似,只是输出变成“Hello World!”。
虽然没有显式请求、编译(或生成)程序项目,但dotnet run命令在执行时隐式执行了这一步。
6.编辑Program.cs文件并修改代码使之和代码清单1.1一致。用Visual Studio Code打开并编辑Program.cs会体验到支持C#的编辑器的好处,代码会用彩色标注不同类型的构造。
若想用Visual Studio Code打开和编辑项目,请在HelloWorld目录中执行
命令。(也可以参考输出1.1,那里展示的命令行命令在Bash和PowerShell中均可使用。)
7.重新运行程序:
输出1.1展示了上述步骤的输出[5]。
输出1.1
使用Visual Studio 2019
在Visual Studio 2019中的操作相似,只是不用命令行,而是用集成开发环境(IDE)。有菜单可选,不必一切都在命令行上进行。
1.启动Visual Studio 2019。
2.在开始页面上点击“Create a new project”按钮。(如果开始页面未出现,请选择“File”|“Start Window”菜单打开开始页面,或者直接通过“File”|“New Project”(Ctrl+Shift+N)菜单创建项目。)
3.在搜索框(Ctrl+E)中输入“Console App”并选择“Console App(.NET Core)——Visual C#”,如图1.2所示。
图1.2 “新建项目”对话框
4.在“Project name”框中输入HelloWorld。在“Location”处选择你的工作目录。如图1.3所示。
5.项目创建好后会打开Program.cs文件供编辑,如图1.4所示。
6.选择“Debug”|“Start Without Debugging”(Ctrl+F5)来生成并运行程序。会显示如输出1.2所示的命令窗口,只是第一行目前为“Hello World! ”。
7.将Program.cs修改成代码清单1.1的样子。
8.返回程序并重新运行,获得如输出1.2所示的结果。
输出1.2
调试
IDE最重要的一个功能是调试。请在Visual Studio或者Visual Studio Code中按照以下额外步骤试验其调试功能:
1.光标定位到System.Console.WriteLine这一行,选择“调试”|“切换断点”(F9)在该行激活断点。
2.选择“调试”|“开始调试” (F5)重新启动应用程序,但这次激活了调试功能。如果你正在使用Visual Studio Code,第一次启动程序时会弹出对话框,询问该程序的运行环境。请选择“.NET Core”,这样Visual Studio Code便会自动生成launch.json和task.json两个与运行有关的配置文件。然后再次选择“调试”|“开始调试”(F5)启动应用程序。注意程序会在断点所在行停止执行。此时可将鼠标放到某个变量(例如args)上以观察它的值。还可以拖动左侧黄箭头将程序执行从当前行移动到当前方法内的另一行。
3.要继续执行,选择“调试”|“继续”(F5)或者点击工具栏上的“继续”按钮。
图1.3 设置新项目的对话框
图1.4 编辑Program.cs文件
若要继续深入学习如何使用Visual Studio 2019进行程序调试,请访问网站http://itl.tc/vsdebugging。
在Visual Studio Code中,程序输出会显示在Debug Console(调试控制台)窗口中。(选择“View”(视图)|“Debug Console”(调试控制台)或Ctrl+Shift+V打开调试控制台观察程序输出结果。)若要继续深入学习如何使用Visual Studio Code进行程序调试,请访问网站https://code.visualstudio.com/docs/editor/debugging。
1.1.2 创建项目
无论dotnet CLI还是Visual Studio都会自动创建几个文件。第一个是名为Program.cs的C#文件。虽然可选择任何名称,但一般都用Program这一名称作为控制台程序起点。.cs是所有C#文件的标准扩展名,也是编译器默认要编译成最终程序的扩展名。为了使用代码清单1.1中的代码,可打开Program.cs文件并将其内容替换成代码清单1.1的内容。保存更新文件之前,注意代码清单1.1和默认生成的代码相比,唯一功能上的差异就是引号间的文本。还有就是后者多了using System;指令,这是一处语义上的差异。
语言对比:Java——文件名必须匹配类名
Java要求文件名和类名一致。C#虽然也常遵守这一约定,却并非必需。在C#中,一个文件可以包含多个类。而且从C# 2.0开始,类的代码可通过所谓的分部类拆分到多个文件中。你了解得越多,就越有能力针对特定应用程序的需求做出最合适的体系结构选择。
虽然并非一定需要,但通常都会为C#项目生成一个项目文件。项目文件的内容随不同应用程序类型和.NET框架而变。但至少会指出哪些文件要包含到编译中、要生成什么应用程序类型(控制台、Web、移动、测试项目等)、支持什么.NET框架、调试或启动应用程序需要什么设置,以及代码的其他依赖项(称为库)。例如,代码清单1.2列出了前面刚创建过的.NET Core控制台应用的项目文件。
代码清单1.2 示例.NET Core控制台应用的项目文件
注意应用程序标识为.NET Core版本3.1(netcoreapp3.1)的控制台应用(Exe)。其他所有设置(比如要编译哪些C#文件)则沿用默认值。例如,和项目文件同一目录(或子目录)中的所有*.cs文件都会包含到编译中。
1.1.3 编译和执行
dotnet build命令生成名为HelloWorld.dll的程序集(assembly)[6]。扩展名.dll代表“动态链接库”(Dynamic Link Library,DLL)。对于.NET Core,所有程序集都使用.dll扩展名。控制台程序也不例外,就像本例这样。.NET Core应用程序的编译输出默认放到子目录./bin/Debug/netcoreapp3.1/。之所以使用Debug这个名称,是因为默认配置就是debug。该配置造成输出为调试进行优化,而不是为性能而优化。编译好的输出本身不能直接执行。相反,需用CLI来寄宿(host)代码。对于.NET Core应用程序,这要求dotnet.exe进程作为应用程序的寄宿进程。这就是为什么总是需要用dotnet run命令来运行应用程序。必要时也可以将应用程序及其所需的运行时文件打包在一起,生成一个可以独立运行且无须另外安装dotnet运行时库可执行文件。具体方法请参考后面的高级主题“发布可独立运行的可执行文件”。
高级主题:发布可独立运行的可执行文件
生成一个可以独立运行,而不依赖于dotnet命令的可执行文件是完全可行的。要做到这一点,需要执行dotnet publish命令,并且通过命令参数--runtime(-r)来指定需要兼容的目标平台。例如,如需兼容大部分Linux平台,可以在CSPROJ文件所在的目录下执行上述命令,并使用linux-x64参数:
执行上述命令后,会生成一个目录(./bin/Debug/netcoreapp3.1/linux-x64/publish/),里面包含了运行HelloWorld程序所需的所有文件。有了这些便不需要在目标平台事先安装dotnet运行时库。若要运行HelloWorld程序,只需要直接运行其可执行文件即可:
(在Windows上,可执行文件会带有扩展名“.exe”,但是在运行可执行文件时不需要写出该扩展名。)
需要注意的是,在上面的示例中生成的可执行文件只能在linux-x64兼容平台上运行。若要支持更多其他平台,则需要为每一个平台生成对应的可执行文件。除了Linux之外,其他目标平台还包括win-x64和osx-x64等。(访问网址http://itl.tc/ridcatalog可以查看兼容平台的完整列表。)
按照前面的方法生成的目录里面,除了应用程序自己的可执行文件之外,还有将近200个依赖文件。实际上也可以生成单一的可独立运行的文件。若要生成单一文件,需要为生成命令加上-p:PublishSingleFile=true参数:
第10章将会介绍更多关于-p参数的内容。
1.1.4 使用本书源代码
本书源代码[7]包含解决方案文件EssentialCSharp.sln,它组合了全书所有代码。Visual Studio和dotnet CLI都能生成、运行和测试这些源代码。
或许最简单的方式是将源代码拷贝到早先创建的HelloWorld程序中并执行。但是,解决方案包含了各章的项目文件,还提供了一个菜单来选择要执行的代码清单。详情参见以下两节。
使用dotnet CLI
要用dotnet CLI生成并执行代码,请打开命令提示符,将当前目录设为EssentialCSharp.sln文件所在的目录。执行dotnet build命令编译所有项目[8]。
要运行特定项目的源代码,导航到项目文件所在目录并执行dotnet run命令。另外,在任何目录都可以执行dotnet run-p <projectfile>命令。其中<projectfile>是要执行的项目文件的路径(例如dotnet run-p .\src\Chapter01\Chapter01.csproj)。随后会运行程序,并提示运行的是哪个代码清单。
许多代码清单都在Chapter[??].Tests目录中提供了相应的单元测试。其中[??]是章的编号。要执行测试,在相应目录中执行dotnet test命令(在EssentialCSharp.sln所在目录执行该命令,则所有单元测试都会执行)。
使用Visual Studio
在Visual Studio中打开解决方案文件后,选择“Build”(生成)|“Build Solution”(生成解决方案)或F6来编译代码。要执行某一章的项目,需要先将该章的项目设为启动项目。例如,要执行第1章的示例,请右击Chapter01项目并选择“Set as StartUp Project”(设为启动项目)。若不这样做,执行时输入非启动项目所在章的代码清单编号会抛出异常。
设置好正确项目后,选择“Debug”(调试)|“Start Without Debugging”(开始执行(不调试))或Ctrl+F5来运行项目。如需调试则按F5。运行时程序会提示输入代码清单的编号(例如18.33)。如前所述,只能输入已启动项目中的代码清单。
许多代码清单都有对应的单元测试。要执行测试,打开测试项目(Chapter[??].Tests),导航到与代码清单对应的测试(比如HelloWorldTests)。双击它在代码编辑器中显示。右击要测试的方法(比如public void Main_InigoHello()),右击并选择“Run Tests”(运行测试)(Ctrl+R, T)或“Debug Tests”(调试测试)(Ctrl+R, Ctrl+T)。
[1] 如果不知道Inigo Montoya是谁,请找《公主新娘》(The Princess Bride)这部电影看看。
[2] C#语言设计者从C/C++规范中删除了他们不喜欢的特性,同时创建了他们喜欢的。开发组还有其他语言的资深专家。
[3] 该命令行工具发布于C# 7.0问世前后。它调用C#编译器csc.exe来编译开发者编写的程序。
[4] https://github.com/PowerShell/PowerShell
[5] 加粗的是由用户输入的内容。
[6] 如果用Microsoft .NET Framework创建控制台应用程序,编译好的代码会放到一个HelloWorld.exe文件中。如已安装Microsoft .NET Framework,可直接执行该文件。
[7] 本书源代码(以及和C#早期版本相关的某些章)可从https://IntelliTect.com/EssentialCSharp下载。推荐直接从GitHub下载,网址是https://github.com/IntelliTect/EssentialCSharp。
[8] 先用Visual Studio 2019编译一遍,因为有些包需要安装。——译者注