快速开始

.NET 平台 和 C#语言

C#(CSharp) 是一种编程语言, 而 .NET 是一个开发平台(运行时 + 标准库 + 工具链)

.NET 平台 是 C# 语言 的开发运行环境。 也可以运行其它编程语言,比如 F#VB.NETIronPython 等等。但是目前主流都是用 C# 语言开发。

这里面 :

🔸 运行时(英文为 CLR)负责加载和执行 C# 代码

🔸 标准库BCL)提供了大量的现成代码,方便我们开发各种功能。

🔸 工具链 包括编译器、打包工具、调试器等,帮助我们编写、编译、调试和发布 C# 程序。

如果说 C# 是车,那 .NET 是就是公路系统,它们紧密相关。

.NET 平台 的历史可以大致分为三个主要时代:

  1. .NET Framework 时代

    这是 .NET 的起点,是 最初 的、 仅限 Windows 的、 闭源 的 实现,

  2. .NET Core 时代

    为了适应云原生和跨平台的需求,2017 年 微软从头开始构建了一个全新的 .NET Core

    这是一个 全新 的、 跨平台 的、 开源 的 重写版本。

    这时候,就有 .NET Framework.NET Core 2 种平台,各自发展,推出各自的新版本。

  3. .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.02002.NET Framework 1.0基础的面向对象特性(类, 结构, 接口, 继承等), 委托, 事件, 属性, unsafe
C# 2.02005.NET Framework 2.0泛型 (Generics), 分部类型 (Partial types), 匿名方法, 可空类型 (Nullable types), 迭代器 (yield)
C# 3.02007.NET Framework 3.5LINQ (语言集成查询), Lambda 表达式, 扩展方法, 匿名类型, 自动属性, var 关键字
C# 4.02010.NET Framework 4.0动态绑定 (dynamic), 命名参数和可选参数, 泛型协变和逆变
C# 5.02012.NET Framework 4.5异步编程模型 (async/ await), 调用方信息特性 (Caller Info Attributes)
C# 6.02015.NET Framework 4.6Roslyn 编译器, 字符串内插, nameof, Null 条件运算符 (?.), 表达式体成员, using static
C# 7.02017.NET Framework 4.7 / .NET Core 1.0元组 (Tuples), 模式匹配 (is, switch), out 变量, 本地函数, ref 返回和 ref 局部变量
C# 7.12017.NET Core 2.0async Main 方法, default 字面量表达式, 推断的元组元素名称
C# 7.22017.NET Core 2.0Span 和 ref 结构体, private protected, 非尾部命名参数
C# 7.32018.NET Core 2.1 / .NET Framework 4.8元组相等性比较, stackalloc 改进, 表达式变量用于更多位置
C# 8.02019.NET Core 3.0可空引用类型, 异步流 (IAsyncEnumerable<T>), 接口的默认实现, switch 表达式, using 声明
C# 9.02020.NET 5记录类型 (record), 顶级语句 (Top-level statements), init-only 属性, 改进的模式匹配
C# 102021.NET 6全局 using 指令 (global using), 文件范围的命名空间, record struct, const 字符串内插
C# 112022.NET 7泛型数学接口, 必需成员 (required), file-local 类型, 原始字符串字面量 ("""...""")
C# 122023.NET 8主构造函数 (Primary Constructors), 集合表达式, using 别名指令可用于任何类型, ref readonly 参数
C# 132024.NET 9参数关键字的默认值(params collections 默认值), 扩展属性模式(ref 扩展属性模式), 锁对象(lock 对象), 反射中的拦截器(reflection 拦截器), 改进的模式匹配(改善的模式匹配), 别名(aliases)
C# 142025.NET 10文件基础应用(file-based apps,支持单文件 C# 脚本和实用工具), 改进的模式匹配和表达式(改进的模式匹配和表达式), 字段主构造函数(field primary constructors), 部分属性(partial properties), WebAssembly 支持增强(WebAssembly 增强)

安装

安装使用 .NET 和 C#, 主要是 安装 .NET SDKIDE 开发环境软件。

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的第三方库。

语句

在 C# 语言中,语句 是构成程序的基本单位。

一个语句通常表示一个完整的操作或指令,告诉计算机执行某个具体的任务。

例如,下面的代码行就是一个语句:

Console.WriteLine("Hello, 白月黑羽!");

关于 Console.WriteLine 具体的解释, 在后面的章节会有详细的学习。

这里我们只要知道 Console.WriteLine(xxx) 这样写,就可以让计算机在屏幕上显示括号里面的内容即可。


有时候一个语句就是一行,但也可以跨多行书写,只要符合语法规则即可。

比如,下面的语句虽然分成了两行,但它们共同构成了一个完整的语句:

Console.WriteLine(
    "Hello, 白月黑羽!"
);

语句的结尾通常以分号 ; 结束,表示该语句的结束。

当然也有不需要加分号的语句,后面会学到。

注释

虽然 C# 的语法很清晰,但我们写的代码为了别人容易看懂,甚至自己以后能看懂,就需要加入一些我们熟悉的 人话,也就是人类语言,辅助理解。

这些辅助理解的人话,就是注释。

C#的注释有两种主要形式:

  1. 单行注释: 以 // 开头,从 // 到该行末尾的内容都是注释。
  2. 多行注释: 以 /* 开头,以 */ 结尾,之间的所有内容都是注释。

比如

// 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, 白月黑羽!");
    }
}

现在只需要使用顶级语句简写, 编译器在背后自动帮你生成了类似的代码。

这样写起来更快捷。


如果你项目中有多个文件的话,缺省只允许一个文件里面使用顶级语句。

因为顶级语句所在的文件就是 整个程序的入口。 入口,只能有一个。