大家好,我是张飞洪,专注.NET开发十来年。感谢您的阅读,我会不定期分享我的学习心得和职场经验,希望我的文章能成为你成长路上的助力。让我们一起精进,共同进步。
本文是《C# 13 与 .NET 9 跨平台开发实战》的第一章,内容很基础,也很细,适合初入.NET职场的新人。
接下来,我们继续第4部分内容学习。
4.使用 Visual Studio 构建控制台应用
概述
本节将演示如何使用 Visual Studio 构建控制台应用。若您没有 Windows 设备或希望使用 VS Code,可以跳过本节(代码完全一致,仅开发体验不同)。但建议您阅读本节内容,其中解释了部分代码实现和顶级程序的工作原理,这些知识适用于所有代码编辑器。
编写代码
创建项目
-
启动 Visual Studio
-
在"新建项目"对话框中:
- 筛选 C# 语言模板
- 搜索框输入
console
- 选择控制台应用 模板
(注意选择跨平台模板,而非仅限 Windows 的 .NET Framework 模板)
-
点击"下一步"
-
项目配置:
- 项目名称:
HelloCS
- 位置:
C:\自定义
- 解决方案名称:
Chapter01
- 项目名称:
-
点击"下一步"
-
附加信息:
- 在框架下拉列表中:
- 注意各 .NET SDK 版本标识(标准支持/长期支持/预览版/已终止支持)
- 选择 .NET 9.0(标准支持)
- 保持"不使用顶级语句"未勾选(本章后续会演示勾选效果)
- 保持"启用原生 AOT 发布"未勾选(第7章将详解此功能)
提示:可通过 .NET 下载页 安装多版本 SDK
- 在框架下拉列表中:
-
点击"创建"
编辑代码
-
若未显示解决方案资源管理器:
视图 → 解决方案资源管理器
-
双击
Program.cs
文件(如图所示)
-
初始代码结构:
csharp
// 参见 https://aka.ms/new-console-template 获取更多信息
Console.WriteLine("Hello, World!");
此模板使用了 C# 9 引入的顶级程序特性(本章后续将详细解释)
如注释所述,可通过此链接了解模板详情
将第 2 行修改为:
Console.WriteLine("Hello, C#!");
编译与运行
接下来就是编译和运行代码:在Visual Studio里,菜单栏选择 调试 → 开始执行(不调试)
最佳实践建议:
在 Visual Studio 中启动项目时,您可以选择是否附加调试器:
-
非调试模式推荐(工具栏空心绿色三角形按钮 →):
- ✅ 资源占用更少
- ✅ 执行速度更快
- ✅ 支持同时启动多个项目
-
调试模式限制(实心绿色三角形按钮 ←):
- ⚠️ 仅限单项目调试
- ⚠️ 多项目调试需开启多个 VS 实例
- ⚠️ 显著增加系统负载
最佳实践 :
除非需要进行代码调试,否则建议始终使用 "开始执行(不调试)" (快捷键
Ctrl+F5
)
查看运行输出
控制台窗口将显示程序执行结果(如图所示)
- 按任意键关闭控制台窗口并返回VS界面
- 关闭属性面板可增加解决方案资源管理器的垂直显示空间
- 双击
HelloCS
项目打开项目文件 - 确认
HelloCS.csproj
中目标框架设置为net9.0
(如图)
编译器生成目录解析
生成目录结构
编译器自动创建两个关键目录:
-
obj 目录
◉ 存储每个源代码文件的编译中间产物
◉ 包含尚未链接的独立对象文件
-
bin 目录
◉ 存放最终生成的二进制可执行文件
◉ (类库项目则包含DLL文件)
将在第7章《.NET类型打包与分发》详细讲解
📌 注意:
- 这些是编译器的工作目录,可随时删除
- 执行"生成"或运行时自动重建
- 不影响项目源代码安全
清理项目
开发者常用清理操作:
-
Visual Studio :
生成 → 清理解决方案
菜单命令 -
CLI 工具 :
bashdotnet clean
理解顶级程序(Top-Level Programs)
现代.NET的代码精简革命
您可能注意到,现代.NET控制台应用的代码异常简洁,这要归功于.NET 6+引入的顶级程序特性:
新旧结构对比
传统结构(.NET 5及更早)
csharp
using System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}
现代结构(.NET 6+)
csharp
// 自动生成的Program类和Main方法
Console.WriteLine("Hello, World!");
编译过程解析
当使用.NET 6+ SDK进行编译时:
- 编译器会自动生成定义
Program
类和Main
方法的样板代码 - 您编写的语句会被自动包裹在这些生成的代码中
版本演进历程
◉ .NET 5 首次引入顶级程序特性
◉ .NET 6 开始默认采用顶级语句模板
◉ .NET 7+ 增加传统风格选项:
切换回传统模式的方法
-
Visual Studio :
勾选"不使用顶级语句"复选框
-
CLI命令:
bashdotnet new console --use-program-main
重要差异警告:
自动生成的代码不会定义命名空间,这将导致:Program类被隐式放置在全局命名空间(空命名空间),与传统项目(自动匹配项目名的命名空间)行为不同
注意:
这个差异可能在大型解决方案中引发类型冲突问题,需特别注意。
顶级程序的核心要求
使用顶级程序时需要特别注意以下关键规则:
文件组织规范
🔹 单一入口文件
每个项目只能有一个包含顶级程序代码的文件
🔹 using指令位置
所有using
命名空间声明必须置于文件顶部
类型声明规则
🔹 类型定义位置
所有类/结构体等类型声明必须放在文件底部
方法命名细节
🔹 入口方法命名
- 显式定义时应命名为
Main
- 编译器自动生成时实际命名为
<Main>$
csharp
// 编译器生成的等效代码
[CompilerGenerated]
internal class Program
{
private static void <Main>$(string[] args)
{
// 您的顶级语句代码
}
}
隐式命名空间导入机制
现象
虽然代码中没有显式包含using System;
语句,Console.WriteLine()
却能正常工作,这是因为:
实现原理
通过C# 10 + .NET 6的新特性组合实现:
- 全局隐式导入机制
- 自动生成的全局引用文件
实际配置
- 在解决方案资源管理器中展开:
obj/
└── Debug/
└── net9.0/
└── HelloCS.GlobalUsings.g.cs - 文件内容示例:
该文件由编译器自动生成,仅当项目目标框架为 .NET 6 及以上版本时出现。它利用了 C# 10 引入的"全局命名空间导入" 功能,一次性把常用的命名空间(如 System)带到整个项目的所有代码文件中,示例如下:
csharp
// <autogenerated />
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;
- 在"解决方案资源管理器"中,点击"显示所有文件"按钮,即可把 bin 和 obj 文件夹重新隐藏起来。
下一章我会进一步讲解"隐式导入"功能。目前只需记住:从 .NET 5 到 .NET 6,大多数项目模板(如控制台模板)都开始使用新的 SDK 和语言特性,把幕后细节"藏"了起来
通过抛出异常揭示隐藏的代码
现在我们来看看编译器生成的隐藏代码是如何编写的:
- 在
Program.cs
中,在输出消息的语句之后,添加一条抛出新异常的语句,如下所示:
csharp
throw new Exception();
-
在 Visual Studio 中,依次选择 调试 (Debug) | 不调试启动 (Start Without Debugging)。
(注意:不要使用调试模式启动,否则异常会被调试器捕获!)
-
控制台窗口的输出将显示应用程序运行的结果,其中包括编译器定义的一个隐藏的 Program 类,
在该类中有一个名为 $ 的方法,并且它包含一个名为 args 的参数用于传递命令行参数。
csharp
Hello, C#!
Unhandled exception. System.Exception: Exception of type 'System.Exception' was thrown.
at Program.<Main>$(String[] args) in C:\cs13net9\Chapter01\HelloCS\Program.cs:line 3

按下任意键关闭控制台应用,并退回到Visual Studio
揭示 Program 类的命名空间
现在我们来看看 Program 类是在哪个命名空间中定义的:
在 Program.cs 文件中,在抛出异常的语句之前,添加如下代码以获取 Program 类的命名空间,并将其输出到控制台:
string name = typeof(Program).Namespace ?? "<null>";
Console.WriteLine($"Namespace: {name}");
这里的 ?? 是 空合并运算符。第一条语句的意思是:
"如果 Program 的命名空间为 null,那么返回 ;否则返回实际的命名空间名称。"
后续章节会进一步解释这些关键字和运算符。现在,请先输入这段代码并运行,观察它的效果。
代码片段(Code Snippets)
代码编辑器通常支持 代码片段 功能,可以让你通过快捷方式快速插入常用代码。
例如,在 Visual Studio 中,要快速输入 Console.WriteLine() 并将光标停留在括号中间(方便直接输入要输出的内容),只需输入 cw,然后按下 Tab、Tab。
👉 建议查阅你所使用的代码编辑器的文档,了解如何通过快捷键插入代码片段
运行并观察结果
1.在 Visual Studio 中,选择菜单:调试 (Debug) → 不调试启动 (Start Without Debugging)
2.控制台窗口输出将显示运行结果,其中可以看到 Program 类被定义时没有命名空间,如下所示:
Namespace: <null>
3.按任意键关闭控制台应用窗口并返回 Visual Studio。
使用 Visual Studio 添加第二个项目
接下来我们向解决方案中添加第二个项目,探索如何处理多项目方案:
1.在 Visual Studio 中,选择菜单:文件 (File) → 添加 (Add) → 新建项目 (New Project)...
⚠️ 注意:
此步骤会将新项目添加到现有解决方案
请不要选择 文件 → 新建 → 项目...,因为那是用来创建全新的"项目+解决方案"的(虽然在对话框下拉框里也能选择"添加到现有解决方案")
2.在 添加新项目 对话框中,在"最近使用的项目模板"中选择 控制台应用 [C#],然后点击 下一步。
3.在 配置新项目 对话框中:
- 项目名称输入:AboutMyEnvironment
- 位置保持:C:\自定义\Chapter01,点击 下一步
3.附加信息 对话框中:框架选择:.NET 9.0 (标准支持),勾选不使用顶级语句 (Do not use top-level statements)
⚠️ 重要提醒:
一定要勾选"不使用顶级语句",这样我们才能看到旧风格的 Program.cs 文件。
- 点击 创建 (Create)。
- 在 AboutMyEnvironment 项目中,打开 Program.cs,你会看到:
- 定义了一个 命名空间(与项目名一致)
- 在命名空间中定义了一个 internal Program 类
- 在类中定义了一个 静态 Main 方法,带有一个名为 args 的参数,返回类型为 void
代码示例如下:
namespace AboutMyEnvironment
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}
- 在
Program.cs
的Main
方法中,将现有的Console.WriteLine
语句替换为以下语句,用于输出:
- 当前目录
- 操作系统的版本
Program
类的命名空间
示例如下代码所示:
csharp
Console.WriteLine(Environment.CurrentDirectory);
Console.WriteLine(Environment.OSVersion.VersionString);
Console.WriteLine(typeof(Program).Namespace);
配置启动项目并运行 AboutMyEnvironment
- 在 Solution Explorer 中,右键点击 Chapter01 解决方案,然后选择 Configure Startup Projects...。
- 在 Solution 'Chapter01' Property Pages 对话框中,将 Startup Project 设置为 Current selection ,然后点击 OK。
- 在 Solution Explorer 中,点击 AboutMyEnvironment 项目(或其中的任何文件/文件夹),注意到 Visual Studio 会将 AboutMyEnvironment 项目名称加粗,表示它已成为启动项目。
💡 最佳实践
推荐使用这种方式设置启动项目,因为它可以很方便地通过**单击一个项目(或项目中的文件)**来切换启动项目。
- 虽然你也可以右键项目并选择 Set as Startup Project,但如果之后要运行另一个项目,就必须手动再次更改。
- 而采用 Current selection 的方式,只需点击项目即可轻松切换。
在大多数章节中,你一次只需要运行一个项目。
在 第 15 章:构建和使用 Web 服务 中,我会展示如何配置多个启动项目。
运行 AboutMyEnvironment
-
在菜单栏中,选择 Debug | Start Without Debugging 来运行 AboutMyEnvironment 项目,并查看运行结果,如以下输出和图所示:
C:\自定义路径\Chapter01\AboutMyEnvironment\bin\Debug\net9.0
Microsoft Windows NT 10.0.26100.0
Namespace: AboutMyEnvironment

在包含两个项目的 VS 解决方案中运行控制台应用程序
Windows 11 只是一个品牌名称。它的正式名称仍然是 Windows NT ,其主版本号依然是 10 !但补丁版本号是 22000 或更高。
- 按任意键关闭控制台应用程序窗口并返回 Visual Studio。
- 当 Visual Studio 运行控制台应用程序时,它会从以下文件夹执行:\bin\Debug\net9.0,在后续章节中,当我们与文件系统交互时,记住这一点非常重要。
- 使用 VS Code (更准确地说是 dotnet CLI)时,行为会有所不同,一会儿就会看到这一点。
5.使用 VS Code 构建控制台应用程序
本节的目标是展示如何使用 VS Code 和 dotnet CLI 来构建一个控制台应用程序。
如果你不打算尝试 VS Code 或 dotnet 命令行工具,可以跳过本节
使用 VS Code 编写代码
让我们开始!
- 启动你常用的文件系统工具,例如 Windows 上的 文件资源管理器 或 Mac 上的 Finder。
- 导航到以下位置之一:
- Windows:
C:
盘 - macOS / Linux:你的用户目录(如
markjprice
或home/markjprice
) - 或者任何你希望保存项目的目录/磁盘。
- Windows:
- 创建一个新文件
- 在该文件夹下,创建一个新文件夹。
创建解决方案
- 在文件夹中,打开命令提示符或终端。
- 例如在 Windows 上,右键点击该文件夹,然后选择 在终端中打开。
- 在命令提示符或终端中,使用 dotnet CLI 创建一个名为
Chapter01
的解决方案,命令如下:
bash
dotnet new sln -n Chapter01
新建解决方案(.sln 文件)
bash
dotnet new sln -n Chapter01-vscode
- -n 或 --name 指定解决方案名字。如果你不写,它就会用当前文件夹名(比如 Chapter01-vscode)。这一步就相当于 先创建一个空的解决方案容器。
新建控制台应用(HelloCS 项目)
bash
dotnet new console -o HelloCS
- -o 或 --output 是要把项目文件放到的文件夹名,同时也是项目名。默认会用你安装的最新 .NET SDK(比如 .NET 9)。如果你要指定 .NET 版本,比如用 .NET 8:
bash
dotnet new console -f net8.0 -o HelloCS
这一步就相当于 在解决方案里新建了一个控制台程序项目。
把项目加到解决方案里
bash
dotnet sln add HelloCS
这会在解决方案文件里登记 HelloCS.csproj。否则解决方案不知道它下面有哪些项目。
输出会显示:
bash
Project `HelloCS\HelloCS.csproj` added to the solution.
用 VS Code 打开当前文件夹
bash
code .
. 表示当前目录。这时 VS Code 会弹个提示:是否信任这个目录里的文件。选"信任"即可。
VS Code 中查看文件
在 EXPLORER(资源管理器)里,展开 HelloCS 文件夹,你会看到:
- HelloCS.csproj(项目文件)
- Program.cs(默认的 C# 程序入口)
- bin、obj(编译时生成的临时文件夹)

图:EXPLORER 显示已创建两个文件和临时文件夹
- 导航到 View | Output(视图 | 输出)。
- 在 OUTPUT(输出) 窗格中,选择 C# Dev Kit,注意工具已经识别并处理了解决方案。
- 在 EXPLORER(资源管理器) 底部,找到 SOLUTION EXPLORER(解决方案资源管理器)。
- 将 SOLUTION EXPLORER 拖到 EXPLORER 窗格的顶部并展开它。
- 在 SOLUTION EXPLORER 中,展开 HelloCS 项目,然后单击名为 Program.cs 的文件,在编辑器窗口中打开它。
- 在 Program.cs 中,修改第 2 行,使写入控制台的文本变为:Hello, C#!
最佳实践:
导航到 File | Auto Save(文件 | 自动保存)。启用此切换开关可以避免每次重新生成应用程序前还要手动保存文件的烦恼。
在前面的步骤中,我向你展示了如何使用 dotnet CLI 来创建解决方案和项目。最后,从 2024 年 8 月或更高版本的 C# Dev Kit 开始,VS Code 提供了改进的项目创建体验,它能让你在 VS Code 中直接使用与 dotnet CLI 相同的项目创建选项。
要启用此功能,你需要修改一个设置,如下所示:
"csharp.experimental.dotnetNewIntegration": true
在 VS Code 中,依次进入 File | Preferences | Settings(文件 | 首选项 | 设置),搜索 dotnet new,然后选中:Csharp > Experimental: Dotnet New Integration 复选框。
你可以在以下链接了解更多:
使用 dotnet CLI 编译和运行代码
接下来的任务是编译并运行代码:
在 SOLUTION EXPLORER 中,右键点击 HelloCS 项目中的任意文件,选择 Open In Integrated Terminal。
在 TERMINAL 中,输入以下命令:
dotnet run
TERMINAL 窗口中的输出会显示运行应用程序的结果。
在 Program.cs 中,在输出消息的语句之后,添加以下语句以获取 Program 类的命名空间名称,将其写入控制台,然后抛出一个新的异常,如下面的代码所示:
string name = typeof(Program).Namespace ?? "<null>";
Console.WriteLine($"Namespace: {name}");
throw new Exception();
在 TERMINAL 中,输入以下命令:
dotnet run
在 TERMINAL 中,你可以按上下箭头循环浏览之前输入的命令,然后按左右箭头编辑命令,最后按 Enter 来运行它们。
TERMINAL 窗口中的输出会显示运行应用程序的结果,包括编译器定义了一个隐藏的 Program 类,其中有一个名为 $ 的方法,它有一个名为 args 的参数用于传递参数,并且它没有命名空间,如下输出所示:
Hello, C#!
Namespace: <null>
Unhandled exception. System.Exception: Exception of type 'System.Exception' was thrown.
at Program.<Main>$(String[] args) in C:\cs13net9\Chapter01-vscode\HelloCS\Program.cs:line 7
使用 VS Code 添加第二个项目
接下来,我们添加第二个项目,探索如何处理多个项目:
在 TERMINAL 中,切换到 Chapter01 目录,如下命令所示:
cd ..
在 TERMINAL 中,创建一个新的控制台应用项目,命名为 AboutMyEnvironment,使用旧的非顶层程序样式,如下命令所示:
dotnet new console -o AboutMyEnvironment --use-program-main
💡 最佳实践:
在 TERMINAL 输入命令时要小心。确保你在正确的文件夹下,再执行可能破坏性的命令!
在 TERMINAL 中,使用 dotnet CLI 将新项目添加到解决方案中,如下命令所示:
dotnet sln add AboutMyEnvironment
注意输出结果,如下所示:
Project `AboutMyEnvironment\AboutMyEnvironment.csproj` added to the solution.
在 SOLUTION EXPLORER 中,打开 AboutMyEnvironment 项目的 Program.cs 文件,然后在 Main 方法中,将现有语句修改为输出当前目录、操作系统版本字符串以及 Program 类的命名空间,如下代码所示:
Console.WriteLine(Environment.CurrentDirectory);
Console.WriteLine(Environment.OSVersion.VersionString);
Console.WriteLine("Namespace: {0}",
typeof(Program).Namespace ?? "<null>");
在 SOLUTION EXPLORER 中,右键点击 AboutMyEnvironment 项目中的任意文件,选择 Open In Integrated Terminal。
在 TERMINAL 中,输入命令运行项目,如下所示:
dotnet run
注意 TERMINAL 窗口中的输出,如下所示:
C:\cs13net9\Chapter01-vscode\AboutMyEnvironment
Microsoft Windows NT 10.0.26100.0
Namespace: AboutMyEnvironment
多终端窗口操作
打开多个终端窗口后,可以通过点击 TERMINAL 面板右侧的终端名称在它们之间切换。
默认名称通常是常用的 shell,如 pwsh、powershell、zsh 或 bash。
右键点击选择 Rename 可更改终端名称。
运行路径差异
当 VS Code(或者更准确地说 dotnet CLI)运行控制台应用时,它会从 文件夹执行。
Visual Studio 会从 \bin\Debug\net9.0 文件夹执行应用。
在后续章节操作文件系统时,需要记住这一点。
如果你在 macOS Ventura 上运行程序,环境操作系统会不同,如下输出:
Unix 13.5.2
💡 最佳实践
虽然源代码(如 .csproj 和 .cs 文件)是相同的,但编译器自动生成的 bin 和 obj 文件夹可能会存在差异,导致错误。如果想在 Visual Studio 和 VS Code 中打开同一个项目,请在另一个编辑器中打开前,先删除临时的 bin 和 obj 文件夹。
这也是为什么本章要求你为 VS Code 项目创建一个不同的文件夹,以避免潜在问题。
VS Code 操作步骤总结
按照以下步骤使用 VS Code 创建解决方案和项目,如表 1.5 所示:
步骤 | 描述 | 命令 |
---|---|---|
1 | 创建解决方案文件夹 | mkdir <solution_folder_name> |
2 | 切换到该文件夹 | cd <solution_folder_name> |
3 | 在文件夹中创建解决方案文件 | dotnet new sln |
4 | 使用模板创建项目及其文件夹 | dotnet new console -o <project_folder_name> |
5 | 将该文件夹及项目添加到解决方案 | dotnet sln add <project_folder_name> |
6 | 重复步骤 4 和 5,创建并添加其他项目 | --- |
7 | 使用 VS Code 打开包含解决方案的当前文件夹路径(. ) |
code . |
表 1:使用 VS Code 创建解决方案和项目的步骤总结
本书中使用的其他项目类型总结
控制台应用程序(Console App / console project)只是项目模板的一种类型。在本书中,你还将使用以下项目模板创建项目,如表 1.6 所示:
编辑器 | dotnet new | Rider -- 类型 |
---|---|---|
Console App | console | Console Application |
Class Library | classlib | Class Library |
xUnit Test Project | xunit | Unit Test Project -- xUnit |
ASP.NET Core Empty | web | ASP.NET Core Web Application -- Empty |
Blazor Web App | blazor | ASP.NET Core Web Application -- Blazor Web App |
ASP.NET Core Web API | webapi | ASP.NET Core Web Application -- Web API |
ASP.NET Core Web API (native AOT) | webapiaot | ASP.NET Core Web Application -- Web API (native AOT) |
表 2:各类代码编辑器对应的项目模板名称
添加项目注意事项
- 添加任何类型新项目到解决方案的步骤都是相同的。
- 唯一不同的是项目模板的类型名称,有时还需要使用一些命令行开关来控制选项。