快速开始
.NET 平台 和 C#语言
C# 一种编程语言, 而 .NET 是一个开发平台(运行时 + 标准库 + 工具链)
.NET 平台 是 C# 语言 的开发运行环境。 也可以运行其它编程语言,比如 F#、VB.NET、IronPython 等等。但是目前主流都是用 C# 语言开发。
这里面 :
🔸 运行时(英文为 CLR)负责加载和执行 C# 代码
🔸 标准库(BCL)提供了大量的现成代码,方便我们开发各种功能。
🔸 工具链 包括编译器、打包工具、调试器等,帮助我们编写、编译、调试和发布 C# 程序。
C# 语言编写的代码, 也可以编译运行在其它平台上,(理论上可编译到 WebAssembly、Native AOT、甚至别的 VM)
如果说 C# 是车,那 .NET 是就是公路系统,它们紧密相关。
.NET 平台 的历史可以大致分为三个主要时代:
-
.NET Framework时代这是 .NET 的起点,是 最初 的、 仅限 Windows 的、 闭源 的 实现,
-
.NET Core时代为了适应云原生和跨平台的需求,2017 年 微软从头开始构建了一个全新的
.NET Core。这是一个 全新 的、 跨平台 的、 开源 的 重写版本。
这时候,就有
.NET Framework和.NET Core2 种平台,各自发展,推出各自的新版本。 -
.NET时代后来,微软不想再让开发者面对2个并行、名字混乱的 .NET 平台, 想“只有一个 .NET”。
从2020 年 11 月 开始,直接推出
.NET 5。从这个版本开始,不再有 "Core" 或 "Framework" 的后缀,只有一个统一的
.NET平台。 发布周期固定为每年 11 月发布一个新主版本。此时,
.NET Core的版本 为3.1,就是它的最后版本了。.NET Framework的版本 为4.8,2022年最后推出4.8.1。 后面可能会有安全补丁,不会再有新功能了。可以说
.NET完全替代了.NET core。 但是.NET Framework由于是预装在 Windows系统里面的,有时为了安装使用程序方便,还会使用它。
C#语言 和 .NET 平台 的 历史版本 对应关系 如下
| 版本 | 发布年份 | .NET 平台 (首次引入) | 关键特性 |
|---|---|---|---|
| C# 1.0 | 2002 | .NET Framework 1.0 | 基础的面向对象特性(类, 结构, 接口, 继承等), 委托, 事件, 属性, unsafe |
| C# 2.0 | 2005 | .NET Framework 2.0 | 泛型 (Generics), 分部类型 (Partial types), 匿名方法, 可空类型 (Nullable types), 迭代器 (yield) |
| C# 3.0 | 2007 | .NET Framework 3.5 | LINQ (语言集成查询), Lambda 表达式, 扩展方法, 匿名类型, 自动属性, var 关键字 |
| C# 4.0 | 2010 | .NET Framework 4.0 | 动态绑定 (dynamic), 命名参数和可选参数, 泛型协变和逆变 |
| C# 5.0 | 2012 | .NET Framework 4.5 | 异步编程模型 (async/ await), 调用方信息特性 (Caller Info Attributes) |
| C# 6.0 | 2015 | .NET Framework 4.6 | Roslyn 编译器, 字符串内插, nameof, Null 条件运算符 (?.), 表达式体成员, using static |
| C# 7.0 | 2017 | .NET Framework 4.7 / .NET Core 1.0 | 元组 (Tuples), 模式匹配 (is, switch), out 变量, 本地函数, ref 返回和 ref 局部变量 |
| C# 7.1 | 2017 | .NET Core 2.0 | async Main 方法, default 字面量表达式, 推断的元组元素名称 |
| C# 7.2 | 2017 | .NET Core 2.0 | Span |
| C# 7.3 | 2018 | .NET Core 2.1 / .NET Framework 4.8 | 元组相等性比较, stackalloc 改进, 表达式变量用于更多位置 |
| C# 8.0 | 2019 | .NET Core 3.0 | 可空引用类型, 异步流 (IAsyncEnumerable<T>), 接口的默认实现, switch 表达式, using 声明 |
| C# 9.0 | 2020 | .NET 5 | 记录类型 (record), 顶级语句 (Top-level statements), init-only 属性, 改进的模式匹配 |
| C# 10 | 2021 | .NET 6 | 全局 using 指令 (global using), 文件范围的命名空间, record struct, const 字符串内插 |
| C# 11 | 2022 | .NET 7 | 泛型数学接口, 必需成员 (required), file-local 类型, 原始字符串字面量 ("""...""") |
| C# 12 | 2023 | .NET 8 | 主构造函数 (Primary Constructors), 集合表达式, using 别名指令可用于任何类型, ref readonly 参数 |
| C# 13 | 2024 | .NET 9 | 参数关键字的默认值(params collections 默认值), 扩展属性模式(ref 扩展属性模式), 锁对象(lock 对象), 反射中的拦截器(reflection 拦截器), 改进的模式匹配(改善的模式匹配), 别名(aliases) |
| C# 14 | 2025 | .NET 10 | 文件基础应用(file-based apps,支持单文件 C# 脚本和实用工具), 改进的模式匹配和表达式(改进的模式匹配和表达式), 字段主构造函数(field primary constructors), 部分属性(partial properties), WebAssembly 支持增强(WebAssembly 增强) |
安装
安装使用 .NET 和 C#, 主要是 安装 .NET SDK 和 IDE 开发环境软件。
Windows 平台上, 最典型的做法就是安装 Visual Studio IDE,安装过程中,同时选择相应的 .NET 开发组件,参考这里
其实,很多时候,我们并不需要 Visual Studio。 除非某些 必须在Windows上的开发场景, 比如 开发一写Windows图形界面软件 需要用 Visual Studio里面的 Windows 窗体设计师、WPF 设计师、WinUI 设计师 和 图形化调试器、性能分析工具。
这个系列教程重点在学习 C# 语言本身,而且有些学员用的是苹果电脑不方便安装 Visual Studio,所以我们使用跨平台的开发方案: VS Code IDE 和 .NET SDK
安装 .NET SDK
点击这里到.NET官网, 点击 下载 .NET SDK 按钮, 然后安装
注意: 这里安装的是 .NET SDK(开发工具包), 而不是 .NET Runtime(运行环境)。
🔸.NET SDK
包含了 .NET Runtime, 以及 编译器、工具链等开发工具。
这是开发者必须安装的。
🔹.NET Runtime
只用来运行已经编译好的程序, 不包含开发工具。
你可以大体看作是 .NET Runtime = CLR(语言引擎) + BCL(标准库)。
这是有时候用户需要安装的。
为什么说有时候呢? 因为我们也可以编译出不需要安装 .NET Runtime 就能运行的程序,后面会讲到。
安装 VS Code IDE
点击这里,到 VS Code 官网,下载安装好 VS Code
然后点击最左侧 Activity Bar 上的 扩展 图标,搜索安装 C# Dev Kit 。 这个扩展 包含了 C# 语言的语法高亮、代码补全、调试支持等功能。
快速开始
开发的 C# 程序,通常需要新建项目,对应一个项目目录,里面除了有代码文件, 还包括项目的配置文件,资源文件等等
为了集中精力在了解编程语言本身,我们先开发最容易理解的 控制台应用程序。
控制台应用程序 让用户操作的界面是 命令行窗口,而不是图形界面。
创建项目目录
进入到项目上层目录,打开一个命令行窗口,执行命令 dotnet new console -n <项目目录名> , 比如
dotnet new console -n Hello
就会创建名为 Hello 的项目目录 和 模板文件。
如果不指定-n 参数,直接 dotnet new console 就会以当前目录为项目目录,目录名为项目名称,只创建模板文件在当前目录中。
其中 console 指定了项目类型(模板), 可以用命令 dotnet new list 查看有哪些类型模板,比如
>dotnet new list
这些模板已匹配你的输入:
模板名 短名称 语言 标记
--------------------------------------- -------------------------- ---------- ----------------------------------
API 控制器 apicontroller [C#] Web/ASP.NET
ASP.NET Core gRPC 服务 grpc [C#] Web/gRPC/API/Service
ASP.NET Core Web API webapi [C#],F# Web/Web API/API/Service
ASP.NET Core Web API (native AOT) webapiaot [C#] Web/Web API/API/Service
ASP.NET Core Web 应用 webapp,razor [C#] Web/MVC/Razor Pages
ASP.NET Core Web 应用(模型-视图-控制器) mvc [C#],F# Web/MVC
ASP.NET Core 空 web [C#],F# Web/Empty
Dotnet 本地工具清单文件 tool-manifest Config
EditorConfig 文件 editorconfig,.editorconfig Config
Windows 窗体应用 winforms [C#],VB Common/WinForms
Windows 窗体控件库 winformscontrollib [C#],VB Common/WinForms
Windows 窗体类库 winformslib [C#],VB Common/WinForms
WPF 应用程序 wpf [C#],VB Common/WPF
WPF 用户控件库 wpfusercontrollib [C#],VB Common/WPF
WPF 类库 wpflib [C#],VB Common/WPF
WPF 自定义控件库 wpfcustomcontrollib [C#],VB Common/WPF
控制台应用 console [C#],F#,VB Common/Console
类库 classlib [C#],F#,VB Common/Library
解决方案文件 sln,solution Solution
辅助角色服务 worker [C#],F# Common/Worker/Web
编写代码,本地运行
然后用 vscode 打开这个目录,你会发现里面有一个代码文件 Program.cs。
这就是 C# 程序的代码文件, 扩展名为 cs。
我们修改的代码为
Console.WriteLine("Hello, 白月黑羽!");
然后点击 vscode 里面的运行按钮,就可以编译运行该项目代码了。
编译产生的可执行程序在 bin\Debug\net<版本号> 目录下
这是一个开发者调试版本的程序,依赖本地安装的 .NET 运行环境 。
✳️ dotnet run 命令
如果你使用其它的IDE,没有运行按钮,也可以在命令行窗口,进入项目根目录,执行如下命令来编译运行代码。
dotnet run
运行它,其实会先执行 dotnet build 编译代码,然后运行编译产生的可执行程序。
✳️ 跳过构建,直接运行已构建的产物
如果你确定代码没有修改,想直接运行上次编译好的程序,可以使用 --no-build 参数,跳过构建步骤,直接运行上次编译好的产物:
dotnet run --no-build
发布程序
将来我们开发各种程序完成后,如果要拷贝给别人使用。
如果别人的电脑上已经安装了 .NET 运行环境,就可以直接拷贝 bin\Debug\net<版本号> 目录下的可执行程序给别人运行。
如果别人的电脑上没有安装 .NET 运行环境,就需要发布出一个可以独立运行的发布版 可执行程序。
新建一个 release.bat 文件(苹果电脑可以叫 release.sh) , 里面是如下内容
dotnet publish -c Release --self-contained true -p:PublishTrimmed=true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:StripSymbols=true -p:EnableCompressionInSingleFile=true
这些参数什么意思,后面的章节会详细讲解。
运行后,产生的可执行文件在 bin\Release\net10.0\win-x64\publish 目录中,可以拷贝给别人运行该文件。
这个可执行程序里面内嵌了一个 .NET 运行环境,所以可执行程序体积有些大,大约 10M 左右
发布 Native AOT 程序
现在的 .NET 编译时支持 Native AOT 编译(提前编译)。
可以简单理解为 连 内嵌.NET 运行环境 都不需要了,就像 C 语言的程序编译出来的代码一样。
一旦你开启 Native AOT(提前编译),单 exe 体积会直接「腰斩甚至砍到脚」,很多真实项目能从 30~40 MB 直接降到 3~10 MB,这就是 当前最硬核的体积压缩方式。
而且启动更快。
新建一个 aot.bat 文件(苹果电脑可以叫 aot.sh) , 里面是如下内容
dotnet publish -c Release --self-contained true -p:PublishAot=true -p:DebugType=None -p:StripSymbols=true
运行后,产生的可执行文件也在 bin\Release\net10.0\win-x64\publish 目录中,可以拷贝给别人运行该文件。
这个可执行程序里面没有内嵌了 .NET 运行环境,所以可执行程序体积很小,大约 1M 左右
当然不是所有的 .NET 程序都可以 这样编译的。典型的是 代码里面用了反射(Reflection.Emit),动态加载 程序集,和使用某些不兼容AOT的第三方库。
直接运行cs代码 👮🏻👮🏻
从 .NET 6(2021 年 11 月)开始,C# 已经彻底支持 不需要新建项目 就能直接写、编译、运行单个 .cs 文件了。
现在这个功能已经非常成熟,几乎所有教程、脚本、教学、面试小 demo 都直接用单文件方式。
我们可以 像python运行py文件那样,直接运行某个.cs文件。
dotnet run 直接运行单个 .cs 文件的功能,实际上是利用了 C# 的顶层语句(Top-level statements) 和 隐式全局 using 指令,以及 单文件编译 的能力。
参考 https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/
在这种模式下,dotnet run 通常只编译和运行指定的那个 .cs 文件。
如果该文件需要使用同一目录下或其他目录下的其他 .cs 文件中定义的类型,有几种方式可以实现:
方式1:将所有相关文件传递给 dotnet run
这是 .NET 团队计划在 .NET 11 中实现的功能
您可以显式地将需要的所有 .cs 文件都列出来,让编译器一起编译它们。
# 假设 main.cs 需要 Helper.cs 和 AnotherHelper.cs
dotnet run main.cs Helper.cs AnotherHelper.cs
这种方式下,所有列出的文件都会被编译到同一个程序集中,因此它们之间可以互相引用(遵循命名空间规则)。
方式2:创建一个临时的、最小化的项目
可以考虑创建一个临时的 .csproj 文件来包含这些文件,然后使用 dotnet run 运行该项目。这不再是纯粹的“单文件”运行,但仍然是一个可行的自动化脚本替代方案。
build vs publish 👮🏻👮🏻
dotnet build compiles the project but doesn't copy dependencies, instead it loads them from the global NuGet cache or installed .NET runtime. So it's not fully independent for deployment.
dotnet publish takes the compiled output and copies all the third-party libraries and project references into the output directory. So the program can run independently without relying on the system's NuGet cache for example.
There are additional options like single-file publishing, trimming, and AOT that can run in dotnet publish but even without those options, the two commands are still different.
The reason dotnet build doesn’t copy dependencies like dotnet publish because it's faster, which is better when you're iterating in development.
语句
在 C# 语言中,语句 是构成程序的基本单位。
一个语句通常表示一个完整的操作或指令,告诉计算机执行某个具体的任务。
例如,下面的代码行就是一个语句:
Console.WriteLine("Hello, 白月黑羽!");
关于 Console.WriteLine 具体的解释, 在后面的章节会有详细的学习。
这里我们只要知道 Console.WriteLine(xxx) 这样写,就可以让计算机在屏幕上显示括号里面的内容即可。
有时候一个语句就是一行,但也可以跨多行书写,只要符合语法规则即可。
比如,下面的语句虽然分成了两行,但它们共同构成了一个完整的语句:
Console.WriteLine(
"Hello, 白月黑羽!"
);
语句的结尾通常以分号 ; 结束,表示该语句的结束。
当然也有不需要加分号的语句,后面会学到。
注释
虽然 C# 的语法很清晰,但我们写的代码为了别人容易看懂,甚至自己以后能看懂,就需要加入一些我们熟悉的 人话,也就是人类语言,辅助理解。
这些辅助理解的人话,就是注释。
C#的注释有两种主要形式:
- 单行注释: 以
//开头,从//到该行末尾的内容都是注释。 - 多行注释: 以
/*开头,以*/结尾,之间的所有内容都是注释。
比如
// info 变量记录作者的信息,包括名字和身高
var info = new Dictionary<string, string>{
{"name", "黑羽白月"},
{"height", "180cm"}
};
// 改变作者的身高记录
info["height"] = "175cm";
Console.WriteLine(info["height"]); // 打印身高到屏幕上
怎么样,有了上面的注释,这些代码是不是更容易读懂了?
我们要注意的是,注释不会对代码的执行有任何影响。
多行注释的例子:
/*
info 变量 记录 作者的信息
包括 名字,身高,体重
*/
var info = new Dictionary<string, string>()
{
{"name", "黑羽白月"},
{"height", "180cm"}
};
顶级语句
前面的示例代码文件里面的内容,很简单
// Program.cs 整个文件就这几行
Console.WriteLine("Hello, 白月黑羽!");
这种写法称之为 : Top-level statements(顶级语句)
这是 C# 9(.NET 5)开始引入的超级好用的语法特性, 从 C# 12(.NET 8)开始几乎所有新项目默认就是这种写法。
在这之前,你必须得定义一个包含 Main 方法的类, 像这样:
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, 白月黑羽!");
}
}
现在只需要使用顶级语句简写, 编译器在背后自动帮你生成了类似的代码。
这样写起来更快捷。
如果你项目中有多个文件的话,缺省只允许一个文件里面使用顶级语句。
因为顶级语句所在的文件就是 整个程序的入口。 入口,只能有一个。
👮🏻👮🏻
🚩 常见用法大全
| 你想干的事 | 顶级语句写法(直接写就行) |
|---|---|
| 接收命令行参数 | var name = args.Length > 0 ? args[0] : "世界";Console.WriteLine($"Hello {name}!"); |
| await 异步 Main | await Task.Delay(1000);Console.WriteLine("我异步执行了!"); |
| 调用别的类的方法 | var service = new UserService();service.Register("小白"); |
| 多行代码、if、循环、try-catch | 随便写,和普通方法里一模一样 |
| 返回退出码 | return 0; 或者 return 100; |
🚩 顶级语句的限制(记住这 4 条就够了)
| 可以做 | 不可以做(会报错) |
|---|---|
| 直接写任何语句(包括 await、if、for) | 不能再定义 Main 方法 |
| 只能有一个 .cs 文件使用顶级语句 | 项目里不能出现第二个文件也用顶级语句(除非加了下面这行) |
| 声明局部变量、调用方法 | 不能在顶级语句里定义类、结构体、接口等类型(要放别的文件) |
使用 args、 return | 不能声明命名空间外的成员(字段、属性等) |
解决方案(几乎所有项目都这么干):
// Program.cs(只放顶级语句)
Console.WriteLine("开始运行");
// 所有类、接口、服务都写到别的文件里
// └─ Services/UserService.cs
// └─ Models/User.cs
// └─ Controllers/UsersController.cs
有顶级语句的文件,也可以包含 命名空间 namespace 和 类型定义 type definitions ,但它们必须位于顶级语句之后。例如:
MyClass.TestMethod();
MyNamespace.MyClass.MyMethod();
public class MyClass
{
public static void TestMethod()
{
Console.WriteLine("Hello World!");
}
}
namespace MyNamespace
{
class MyClass
{
public static void MyMethod()
{
Console.WriteLine("Hello World from MyNamespace.MyClass.MyMethod!");
}
}
}
🚩 允许有多个顶级语句文件(高级技巧)
如果你非要在多个文件里写顶级语句,加这一行到 .csproj 就行了:
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- 加上这行就允许多个顶级文件 -->
<EnableTopLevelMultipleFiles>true</EnableTopLevelMultipleFiles>
</PropertyGroup>
但 99.9% 的项目都不需要这行,因为大家都只在 Program.cs 用顶级语句。