C# 概述与环境配置

77 minBeginner2026/7/21

C# 语言概述、.NET 生态、.NET 8/9 新特性、环境搭建与 Hello World

C# 概述与环境配置

本文是 FANDEX C# 系列的开篇。我们将从语言的诞生背景、设计哲学、运行平台、生态构成,到本地环境的搭建与第一个可运行程序,全方位带你建立对 C# 与 .NET 的宏观认知。内容对标 MIT 6.0001、Stanford CS106X、CMU 15-110 等海外名校入门课程的教学严谨度,同时融入企业级工程实战经验,支持 0 基础自学。


目录

  1. 学习目标(Bloom 分类法)
  2. 历史动机与演化
  3. 形式化定义
  4. 理论推导与证明
  5. 代码示例
  6. 对比分析
  7. 常见陷阱与反模式
  8. 工程实践与最佳实践
  9. 案例研究
  10. 习题与思考题
  11. 参考文献
  12. 延伸阅读

1. 学习目标(Bloom 分类法)

Bloom 分类法将认知能力由低到高划分为六个层次:记忆(Remember)、理解(Understand)、应用(Apply)、分析(Analyze)、评价(Evaluate)、创造(Create)。本节据此列出本篇学习目标,便于读者自检与教学评估。

1.1 记忆(Remember)

  • R1:能复述 C# 语言的诞生年份(2000 年)、首席设计师(Anders Hejlsberg)及其前序作品(Turbo Pascal、Delphi、TypeScript)。
  • R2:能列出 .NET 平台的三大核心组成:运行时(Runtime)、基础类库(BCL, Base Class Library)、应用模型(App Model)。
  • R3:能背诵 .NET 主要版本的时间线(.NET Framework 1.0/2.0/3.5/4.x、.NET Core 1.0/2.0/3.x、.NET 5/6/7/8/9)。
  • R4:能识别 SDK(Software Development Kit)与 Runtime(运行时)的语义差异。

1.2 理解(Understand)

  • U1:能解释 CTS(Common Type System)、CLS(Common Language Specification)、CIL(Common Intermediate Language)三者的层次关系。
  • U2:能用自己的话阐述托管代码(Managed Code)与原生代码(Native Code)的执行差异。
  • U3:能说明 JIT(Just-In-Time)编译与 AOT(Ahead-Of-Time)编译的优劣。
  • U4:能描述 NuGet 包管理器在依赖解析中的作用机制。
  • U5:能阐述 .NET 跨平台(Windows / Linux / macOS / iOS / Android)的实现原理。

1.3 应用(Apply)

  • A1:能在 Windows / macOS / Linux 上独立安装 .NET SDK 9.0。
  • A2:能使用 dotnet newdotnet builddotnet run 完成控制台应用的创建、编译、运行。
  • A3:能使用 dotnet add package 引入 NuGet 包并正确调用。
  • A4:能配置 VS Code / Rider / Visual Studio 2022 的 C# 开发环境。

1.4 分析(Analyze)

  • An1:能对比 .NET Framework 与 .NET(Core)在 GC、AppDomain、程序集加载机制上的差异。
  • An2:能拆解 dotnet build 背后的 MSBuild 任务链(Restore → Compile → Pack → Publish)。
  • An3:能分析 csprojProgram.csbin/obj/ 之间的依赖关系。

1.5 评价(Evaluate)

  • E1:能评估在何种场景下应选择 .NET 而非 JVM、Go、Node.js。
  • E2:能评判 AOT 编译在启动速度与运行时性能之间的权衡是否合理。
  • E3:能评估 LTS(Long Term Support)版本与 STS(Standard Term Support)版本在生产环境中的选型。

1.6 创造(Create)

  • C1:能设计一个具备项目分层(Presentation / Business / Data)的 C# 控制台应用骨架。
  • C2:能为团队编写一份《C# 开发环境搭建指南》文档,涵盖版本管理、IDE 配置、调试技巧。

2. 历史动机与演化

理解一门语言的现在,必须先理解它走过的路。C# 的演化是一部”在工程实用主义与语言创新之间不断平衡”的历史。

2.1 史前背景:COM 时代的痛点

在 .NET 诞生之前(1990 年代),微软生态主流是 COM(Component Object Model)与 MFC(Microsoft Foundation Classes)。开发者面临的核心痛点包括:

  1. 语言割裂:C++、VB6、Delphi 各自为政,组件互操作需通过 COM 的 IUnknownIDispatchBSTRSAFEARRAY 等繁杂 ABI,易错且低效。
  2. 内存管理混乱:C++ 需手工管理 new/delete,VB6 采用引用计数(Reference Counting),两者协作时易出现循环引用导致的内存泄漏。
  3. 跨语言调用成本高:C++ 写的 COM 组件被 VB 调用时需要 IDL(Interface Definition Language)转译,开发体验差。
  4. Java 的崛起:1995 年 Sun 推出 Java,凭借”Write Once, Run Anywhere”与垃圾回收(GC)抢占企业市场,对微软构成战略威胁。

2.2 .NET Framework 1.0(2002)与 C# 1.0

2000 年 6 月,微软在 Orlando 召开的 PDC(Professional Developers Conference)上正式发布 .NET 战略(Project Lightning / NGWS, Next Generation Windows Services)。其核心目标:

  • 统一所有微软语言运行于同一虚拟机(CLR, Common Language Runtime)。
  • 提供统一的类型系统(CTS)与统一类库(BCL)。
  • 引入垃圾回收(GC)解决 COM 的内存泄漏问题。
  • 推出新一代语言 C#(发音 C-Sharp,源自音乐符号 ”♯” 表示升半音),由 Anders Hejlsberg 主导设计。

Anders Hejlsberg 在加入微软前,是 Borland Turbo Pascal 与 Delphi 的首席架构师。他在 C# 设计中融合了:

  • C++ 的语法外观与性能。
  • Delphi 的组件化思想。
  • Java 的 GC 与平台中立理念。
  • VB 的快速开发体验。

C# 1.0(2002 年发布) 的核心特性:

  • 类(Class)、结构(Struct)、接口(Interface)、委托(Delegate)、事件(Event)。
  • 属性(Property)、索引器(Indexer)、运算符重载(Operator Overloading)。
  • 特性(Attribute)。
  • 基于 foreach 的迭代器模式。
  • 完整的 GC 与 AppDomain 隔离。

2.3 .NET Framework 2.0(2005)与 C# 2.0

C# 2.0 引入了影响深远的特性:

  • 泛型(Generics):在 CLR 层而非语法糖层实现,支持运行时类型擦除的 Java 泛型在性能与类型安全上无法与之相比。
  • 可空类型(Nullable Types)int? x = null; 解决了值类型无法表达”未知”的问题。
  • 迭代器(Iterators)yield return 让自定义可枚举集合极其简洁。
  • 匿名方法(Anonymous Methods)delegate(int x) { return x*x; },为后续 Lambda 铺路。
  • 协变与逆变(Covariance/Contravariance):在委托层面初步支持。

2.4 .NET Framework 3.0 / 3.5(2006 / 2007)与 C# 3.0

C# 3.0 是该语言的”二次飞跃”,伴随 .NET Framework 3.5 推出,LINQ(Language Integrated Query)登场。核心特性:

  • LINQ to Objects / SQL / XML:统一查询语法,将数据库查询纳入语言层面。
  • Lambda 表达式x => x * x 取代冗长的匿名方法。
  • 扩展方法(Extension Methods)public static int Square(this int x) 让类型在不被继承的情况下”扩展”。
  • 匿名类型(Anonymous Types)new { Name = "Alice", Age = 30 }
  • 隐式类型(var):编译期类型推断。
  • 对象/集合初始化器new List<int> { 1, 2, 3 }
  • 表达式树(Expression Trees)Expression<Func<int,int>>,将代码作为数据,是 EF Core、IQueryable 的基石。
  • 自动实现属性public int X { get; set; }

2.5 .NET Framework 4.0(2010)与 C# 4.0

为应对动态语言(Python、Ruby)的崛起:

  • 动态类型(dynamic):基于 DLR(Dynamic Language Runtime),与 IronPython、IronRuby 互操作。
  • 命名参数与可选参数M(x: 1, y: 2)
  • 泛型协变/逆变IEnumerable<out T>,让 IEnumerable<string> 可赋值给 IEnumerable<object>
  • 嵌入式 COM 互操作:Office 自动化体验大幅提升。
  • TPL(Task Parallel Library)与 PLINQ:基于 Task 的并行编程范式,奠定后续 async/await 基础。

2.6 .NET Framework 4.5(2012)与 C# 5.0

C# 5.0 是异步编程的里程碑

  • async / await:将复杂的 CPS(Continuation-Passing Style)变换封装为语言特性,使异步代码”长得像同步代码”。
  • Caller Information 特性[CallerMemberName][CallerFilePath][CallerLineNumber],简化 INotifyPropertyChanged 实现。
  • foreach 与闭包改进:修复了 C# 5 之前闭包捕获循环变量的著名 bug。

2.7 .NET Framework 4.6(2015)与 C# 6.0

C# 6.0 以”小而精”的特性提高开发体验:

  • 静态导入(using static)
  • 异常过滤器(Exception Filters)catch (Exception e) when (e.Code == 404)
  • 自动属性初始化器public int X { get; set; } = 42;
  • 只读自动属性public int X { get; }
  • 表达式主体成员public int Square(int x) => x * x;
  • null 条件运算符person?.Address?.City
  • 字符串插值$"{name} is {age}"
  • nameof 表达式nameof(x)
  • 索引初始化器new Dictionary<int,string> { [1]="a", [2]="b" }

2.8 .NET Framework 4.7(2017)与 C# 7.0 / 7.1 / 7.2 / 7.3

C# 7.x 围绕”性能”与”模式匹配”展开:

  • 元组(Tuples)与解构(string Name, int Age) = GetPerson();
  • 模式匹配(Pattern Matching)if (o is int i)case int i when i > 0
  • 本地函数(Local Functions):方法内定义方法。
  • ref 返回与本地变量ref int Find(int[] arr)
  • out var 声明if (int.TryParse(s, out var n))
  • 通用异步返回(Generalized Async Return)ValueTask<T>
  • readonly struct / ref struct / in 参数:高性能场景下的零拷贝编程。
  • Span 与 Memory:C# 7.2 起 Span<T> 作为 ref struct 提供,是 .NET 性能革命的核心。
  • 数字分隔符与二进制字面量0b1010_1010

2.9 .NET Core 1.0 / 2.0 / 2.1 / 2.2(2016 - 2018)

为应对容器化与跨平台需求,微软启动 .NET Core 重写:

  • 跨平台:从 Windows-only 到 Linux / macOS 一等公民。
  • 模块化:通过 NuGet 按需引入,告别庞大的 .NET Framework 单体安装。
  • 高性能:Server GC、Span、Pipeline 优化。
  • ASP.NET Core:完全重写,抛弃 System.Web,性能比 ASP.NET 4.x 提升数倍。
  • EF Core:抛弃复杂的 EDMX,转向 Code-First 与 DbContext。
  • CLI 优先dotnet new / build / run / test / publish 一套命令覆盖全流程。

C# 7.x 在此阶段发布,配合 .NET Standard 2.0(2017)解决类库跨 Framework 与 Core 共享问题。

2.10 .NET Core 3.0 / 3.1(2019)

  • Windows 桌面支持回归:WPF、WinForms 在 .NET Core 3.0 复活。
  • 默认可空引用类型(NRT):C# 8.0 引入,编译期 null 安全。
  • 异步流(IAsyncEnumerableawait foreach
  • 索引与范围(Index / Range)arr[^1]arr[1..3]
  • switch 表达式:模式匹配的”函数式”形态。
  • 默认接口方法:接口可包含实现。
  • unmanaged 泛型约束where T : unmanaged

2.11 .NET 5(2020)与 C# 9.0

微软统一品牌:从此不再有 ”.NET Core”,只有 ”.NET”。

  • 记录类型(record)record Person(string Name, int Age); 不可变值对象一行搞定。
  • 顶级语句(Top-Level Statements)Program.cs 只需 Console.WriteLine("Hello");
  • init 访问器public int X { get; init; },对象初始化器阶段可写、之后只读。
  • 模式匹配增强andornot 模式组合。
  • 目标类型 newPerson p = new();
  • 协变返回类型:子类 override 可返回更窄类型。
  • *函数指针(delegate)**:非托管调用零开销。
  • 源生成器(Source Generators):编译期代码生成,替代反射。

2.12 .NET 6(2021 LTS)与 C# 10.0

  • 全局 usingglobal using System.Linq;
  • 文件范围命名空间namespace MyApp; 一行声明。
  • record struct:值类型 record。
  • 常量字符串插值const string s = $"x";(受限于常量表达式)。
  • 结构无参构造struct S { public S() {} }
  • CallerArgumentExpression:用于参数名诊断。
  • Hot Reload:运行时修改代码即时生效。
  • 最小 API(Minimal API)app.MapGet("/", () => "Hello");
  • MAUI:跨平台 UI 框架(Android、iOS、Windows、macOS)。

2.13 .NET 7(2022 STS)与 C# 11.0

  • 原始字符串字面量"""...""" 支持多行与插值。
  • 列表模式[1, 2, .., 5] 模式匹配。
  • required 成员required public string Name { get; set; }
  • 泛型数学支持(Generic Math)interface IAdditionOperators<TSelf, TOther, TResult>,运算符重载泛型化。
  • file 作用域类型file class Helper 仅文件内可见。
  • UTF-8 字符串字面量"hello"u8 返回 ReadOnlySpan<byte>
  • ref struct 改进:可扩展更多场景。
  • Native AOT:实验性发布 AOT 编译。

2.14 .NET 8(2023 LTS)与 C# 12.0

  • 主构造函数(Primary Constructor)class Person(string name)
  • 集合表达式int[] a = [1, 2, 3];
  • ref struct 泛型约束where T : allows ref struct
  • 别名任意类型using Point = (int X, int Y);
  • 内联数组[InlineArray(4)] struct Vec4 { private int _value; }
  • Experimental 特性:标记实验 API。
  • 时间抽象TimeProvider,便于测试时控时。
  • ASP.NET Core 8 重大性能优化:Kestrel 与 AOT 支持最小 API。
  • Native AOT 正式可用:发布时无运行时依赖。

2.15 .NET 9(2024 STS)与 C# 13.0

  • params 集合增强params ReadOnlySpan<int>
  • lock 类型化lock(obj) 使用 System.Threading.Lock
  • partial 属性:源生成器场景使用。
  • field 上下文关键字public string Name { get; set => field = value; } 自动 backing field。
  • ref struct 协变ref struct 可作为 scoped 参数。
  • 泛型 overload resolution 改进
  • Workloads of AOT:减小体积。
  • ASP.NET Core 9 静态资产交付优化
  • HybridCache:分布式缓存抽象。
  • OpenTelemetry 内置支持
  • Pgo(Profile-Guided Optimization)改进:JIT 性能再升 15%。

2.16 演化时间线总览

年份.NET 版本C# 版本标志性特性LTS/STS
2002.NET Framework 1.01.0CLR、C# 诞生-
2005.NET Framework 2.02.0泛型、可空类型-
2007.NET Framework 3.53.0LINQ、Lambda-
2010.NET Framework 4.04.0dynamic、TPL-
2012.NET Framework 4.55.0async/await-
2015.NET Framework 4.66.0字符串插值、null 条件-
2017.NET Framework 4.7 / Core 2.07.x元组、模式匹配、Span-
2019.NET Core 3.18.0NRT、异步流LTS
2020.NET 59.0record、顶级语句STS
2021.NET 610.0MAUI、最小 APILTS
2022.NET 711.0泛型数学、原始字符串STS
2023.NET 812.0主构造、Native AOTLTS
2024.NET 913.0HybridCache、PGOSTS

2.17 设计哲学小结

C# 的演化贯穿三大原则:

  1. 实用主义:每项新特性都源于真实工程痛点,而非纯学术理想。
  2. 类型安全优先:通过编译期检查(如 NRT、模式匹配)消灭运行时错误。
  3. 性能不退步:任何抽象都可在性能关键场景被”穿透”(如 Span<T>ref structValueTaskUnsafe)。

理解这三点,能帮你预测未来 C# 演化方向。


3. 形式化定义

3.1 C# 语言的形式化定义

C# 是一种静态类型、强类型、面向对象、多范式、托管执行的编程语言。下面从形式化角度拆解这些性质。

3.1.1 类型系统

T\mathcal{T} 为类型集合,Γ\Gamma 为类型环境(typing context),表达式 ee 的类型推断关系记为 Γe:τ\Gamma \vdash e : \tau。C# 的类型规则可形式化为:

Γe1:τ1Γe2:τ2τ1τ2Γe1+e2:τ1(T-Add)\frac{\Gamma \vdash e_1 : \tau_1 \quad \Gamma \vdash e_2 : \tau_2 \quad \tau_1 \equiv \tau_2}{\Gamma \vdash e_1 + e_2 : \tau_1} \quad (\text{T-Add})

其中 τ1τ2\tau_1 \equiv \tau_2 表示类型相容(含隐式转换)。

C# 类型系统在 Hindley-Milner 之上做了若干扩展:

  • 子类型(Subtyping)τ1<:τ2\tau_1 <: \tau_2 表示 τ1\tau_1τ2\tau_2 的子类型。
  • 协变(Covariance):若 IEnumerable<out T>,则 T<:UIEnumerable<T><:IEnumerable<U>T <: U \Rightarrow \text{IEnumerable}<T> <: \text{IEnumerable}<U>
  • 逆变(Contravariance):若 Action<in T>,则 T<:UAction<U><:Action<T>T <: U \Rightarrow \text{Action}<U> <: \text{Action}<T>
  • 可空性(Nullability):引用类型 TT 拆为 TT(不可空)与 T?T?(可空)。

3.1.2 语法与文法

C# 文法以 LL(k) 与 LR 混合方式解析。其文法可表示为:

CompilationUnitExternAliasListoUsingListoGlobalAttributesoNamespaceMemberListoNamespaceMemberNamespaceDeclTypeDeclTypeDeclClassDeclStructDeclInterfaceDeclEnumDeclDelegateDeclRecordDecl\begin{aligned} \text{CompilationUnit} &\to \text{ExternAliasList}_o \, \text{UsingList}_o \, \text{GlobalAttributes}_o \, \text{NamespaceMemberList}_o \\ \text{NamespaceMember} &\to \text{NamespaceDecl} \mid \text{TypeDecl} \\ \text{TypeDecl} &\to \text{ClassDecl} \mid \text{StructDecl} \mid \text{InterfaceDecl} \mid \text{EnumDecl} \mid \text{DelegateDecl} \mid \text{RecordDecl} \end{aligned}

详细文法见 C# Language Specification(Dotnet GitHub 仓库 dotnet/csharpstandard)。

3.2 .NET 平台的形式化定义

.NET 平台可形式化为三元组:

.NET=CLR,BCL,AppModel\text{.NET} = \langle \text{CLR}, \text{BCL}, \text{AppModel} \rangle

其中:

  • CLR(Common Language Runtime):执行引擎,提供 GC、JIT、类型安全、异常处理等运行时服务。
  • BCL(Base Class Library):基础类库,包含集合、IO、网络、LINQ、反射等。
  • AppModel(Application Model):应用模型,如 ASP.NET Core、EF Core、MAUI、WPF、WinForms。

3.3 程序执行流程的形式化

C# 源代码的执行流程可形式化为映射链:

SourceC# Compiler (Roslyn)CILJIT / AOTMachine CodeCPUResult\text{Source} \xrightarrow{\text{C\# Compiler (Roslyn)}} \text{CIL} \xrightarrow{\text{JIT / AOT}} \text{Machine Code} \xrightarrow{\text{CPU}} \text{Result}

详细步骤:

  1. 源代码.cs 文件,人类可读。
  2. 词法分析 → 语法分析:生成抽象语法树(AST)。
  3. 语义分析:类型检查、可空性分析、特性应用。
  4. CIL 生成:输出 .dll / .exe,内容为 Common Intermediate Language(字节码)。
  5. 类加载:CLR 加载程序集,验证类型安全。
  6. JIT 编译:方法首次调用时,JIT 将 CIL 编译为本地机器码。
  7. 执行:CPU 执行机器码,GC 在后台管理内存。

3.4 内存模型形式化

.NET 的托管堆(Managed Heap)可形式化为:

H={(ai,ti,gi,si)aiAddr,tiType,gi{0,1,2,LOH},siN}\mathcal{H} = \{(a_i, t_i, g_i, s_i) \mid a_i \in \text{Addr}, t_i \in \text{Type}, g_i \in \{0,1,2,\text{LOH}\}, s_i \in \mathbb{N}\}

其中 aia_i 为地址,tit_i 为类型,gig_i 为所属 GC 代(Generation 0/1/2/LOH),sis_i 为大小。

GC 的回收策略可表示为:

Collect(g):HHwhere g{0,1,2}\text{Collect}(g) : \mathcal{H} \to \mathcal{H} \quad \text{where } g \in \{0,1,2\}

回收后保留的对象晋升一代:

(a,t,gi,s)Hsurvived:gi=min(gi+1,2)\forall (a, t, g_i, s) \in \mathcal{H}_{\text{survived}} : g_i' = \min(g_i + 1, 2)

3.5 异步执行模型形式化

async/await 的状态机重写可形式化为变换 Tasync\mathcal{T}_{\text{async}}

Tasync:AsyncMethodStateMachine\mathcal{T}_{\text{async}} : \text{AsyncMethod} \to \text{StateMachine}

其中 StateMachine\text{StateMachine} 是一个有限状态机 (S,s0,Σ,δ,F)(S, s_0, \Sigma, \delta, F)

  • SS:状态集合,每个 await 点对应一个状态。
  • s0Ss_0 \in S:初始状态。
  • Σ\Sigma:输入符号(awaitable 完成事件)。
  • δ:S×ΣS\delta : S \times \Sigma \to S:状态转移函数。
  • FSF \subseteq S:终止状态集合。

详细推导见本系列《async-await 状态机》一文。


4. 理论推导与证明

4.1 类型安全性证明(Sketch)

C# 类型系统满足 ProgressPreservation 两大性质(受 Erasure 影响,对泛型稍作调整)。

定理 4.1(Progress):对于良型表达式 Γe:τ\Gamma \vdash e : \tauee 要么是一个值,要么存在 ee' 使得 eee \to e'

证明(Sketch):对 ee 的结构归纳。

  • ee 是值(变量、字面量),成立。
  • e=e1+e2e = e_1 + e_2,由归纳 e1e_1 要么是值要么可归约;若 e1e_1 可归约,则 ee1+e2e \to e_1' + e_2;若 e1=v1e_1 = v_1 是值,同理处理 e2e_2;若两者都是值,依据算术运算法则可归约。

定理 4.2(Preservation):若 Γe:τ\Gamma \vdash e : \taueee \to e',则 Γe:τ\Gamma \vdash e' : \tau

证明(Sketch):对归约步骤分类讨论。

  • 算术:Γv1+v2:int\Gamma \vdash v_1 + v_2 : \text{int},归约为 vvv1+v2v_1 + v_2 的算术结果),Γv:int\Gamma \vdash v : \text{int} 成立。
  • 函数调用:(λx.e)ve[x:=v](\lambda x.e) \, v \to e[x := v],由代入引理 Γ,x:τ1e:τ2Γe[x:=v]:τ2\Gamma, x : \tau_1 \vdash e : \tau_2 \Rightarrow \Gamma \vdash e[x := v] : \tau_2

4.2 协变/逆变安全性证明

定理 4.3(协变安全):若 interface I<out T>,且 T<:UT <: U,则 I<T> <: I<U> 类型安全。

证明:协变要求 TI 中只出现在输出位置(返回类型)。设 I<T> 暴露方法 T M(),对 I<T> 实例 o 调用 o.M() 返回 TT 实例。若将 o 视为 I<U>,调用 o.M() 期望返回 UU。由 T<:UT <: U,返回的 TT 自动是 UU,类型安全成立。

定理 4.4(逆变安全):若 interface I<in T>,且 T<:UT <: U,则 I<U> <: I<T> 类型安全。

证明:逆变要求 T 只出现在输入位置。设 I<T> 暴露 void M(T x),对 I<T> 实例 o 调用 o.M(x) 要求 x:Tx : T。若将 o 视为 I<U>(即接受 U 类型参数),调用 o.M(y) 其中 y:Uy : U。由于 o 实际是 I<T> 实例,要求 x:Tx : T,而 T<:UT <: U 意味着 UU 实例不一定是 TT,这看似不安全——但逆变的方向相反:I<U> <: I<T> 意味着 I<U> 可用作 I<T>。此时调用 o.M(x) 其中 x:Tx : T,由 T<:UT <: Uxx 也是 UU,传给 I<U>M(U) 安全。证毕。

4.3 GC 暂停时间复杂度

命题 4.5:.NET 的分代 GC 第 ii 代回收时间 TiT_i 与该代对象数量 nin_i 近似成正比,即 Ti=O(ni)T_i = O(n_i)

推导:分代 GC 采用标记-压缩(Mark-Compact)或标记-复制(Mark-Copy)算法。

  • 标记阶段:从 GC Root 出发,BFS 遍历所有可达对象,复杂度 O(E)O(|E|)E|E| 为对象引用边数。
  • 压缩/复制阶段:遍历存活对象,更新引用,复杂度 O(nisurvived)O(n_i^{\text{survived}})

由于第 0 代存活率低(多数对象短命),n0survivedn0n_0^{\text{survived}} \ll n_0,故 T0T_0 极小。第 2 代(Gen 2)回收涉及全堆扫描,T2T_2 较大。

4.4 JIT 编译优化

命题 4.6:JIT 编译产生的代码性能 PJITP_{\text{JIT}} 满足 PJITPAOTϵP_{\text{JIT}} \geq P_{\text{AOT}} - \epsilon,其中 ϵ\epsilon 为预热开销。

直觉:JIT 拥有 AOT 不具备的运行时信息:

  • 类型 profile:哪些虚方法实际只有一个实现,可去虚化(Devirtualization)。
  • 分支 profile:哪些分支几乎不执行,可优化布局。
  • 内联:跨程序集边界的内联由 JIT 决定。

Profile-Guided Optimization(PGO)在 .NET 6+ 中将 JIT 优化推向新高度。.NET 9 的动态 PGO 进一步使 PJIT>PAOTP_{\text{JIT}} > P_{\text{AOT}} 的场景比例提升。

4.5 Roslyn 编译流水线

Roslyn 编译器流水线可形式化为:

SourceLexerTokensParserSyntaxTreeBinderBoundTreeEmitterCIL\text{Source} \xrightarrow{\text{Lexer}} \text{Tokens} \xrightarrow{\text{Parser}} \text{SyntaxTree} \xrightarrow{\text{Binder}} \text{BoundTree} \xrightarrow{\text{Emitter}} \text{CIL}

每一阶段都暴露为公开 API(Microsoft.CodeAnalysis.CSharp),使源生成器(Source Generators)、分析器(Analyzers)、代码修复(Code Fixes)成为可能。


5. 代码示例

5.1 安装 .NET SDK

5.1.1 Windows 安装

方式一:官方安装包

下载地址:https://dotnet.microsoft.com/zh-cn/download/dotnet/9.0

选择 SDK 9.0.x 的 Windows x64 安装包(dotnet-sdk-9.0.xxx-win-x64.exe),双击安装即可。安装包会自动配置 PATH 与注册表。

方式二:winget

winget install Microsoft.DotNet.SDK.9

方式三:Chocolatey

choco install dotnet-sdk -y

5.1.2 macOS 安装

# Homebrew
brew install --cask dotnet-sdk

5.1.3 Linux(Ubuntu 22.04)

# 添加微软源
wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb

# 安装 SDK
sudo apt-get update && sudo apt-get install -y dotnet-sdk-9.0

5.1.4 验证安装

dotnet --version
dotnet --list-sdks
dotnet --list-runtimes

期望输出:

9.0.100
9.0.100 [/usr/local/share/dotnet/sdk]
Microsoft.NETCore.App 9.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.AspNetCore.App 9.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]

5.2 第一个控制台应用

5.2.1 创建项目

mkdir HelloFandex && cd HelloFandex
dotnet new console -n HelloFandex -o .
dotnet run

dotnet new console 生成如下结构:

HelloFandex/
├── HelloFandex.csproj
├── Program.cs
└── obj/
    └── HelloFandex.csproj.nuget.g.props

5.2.2 默认 Program.cs

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

这是 C# 9.0 之后的顶级语句形式,等价于:

using System;

namespace HelloFandex;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello, World!");
    }
}

5.2.3 csproj 文件解读

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <RootNamespace>HelloFandex</RootNamespace>
    <AssemblyName>HelloFandex</AssemblyName>
  </PropertyGroup>

</Project>
字段含义
Sdk="Microsoft.NET.Sdk"使用 .NET 默认 SDK,决定默认引用程序集与 build 任务
OutputTypeExe 表示可执行;Library 表示类库
TargetFramework目标框架:net9.0net8.0netstandard2.0
ImplicitUsings是否自动 using 常用命名空间
Nullable启用可空引用类型检查

5.3 编译与运行机制

5.3.1 编译

dotnet build

执行流程:

  1. Restore:解析 NuGet 引用,写入 obj/project.assets.json
  2. GenerateAssemblyInfo:生成 AssemblyVersion 等元数据。
  3. Csc:调用 Roslyn 编译器,输出 CIL 到 bin/Debug/net9.0/HelloFandex.dll
  4. CopyFilesMarkedCopyLocal:拷贝 NuGet 依赖到输出目录。

5.3.2 运行

dotnet run

dotnet run 等价于 dotnet build && dotnet <assembly.dll>,是开发期最常用命令。

5.3.3 发布

# 框架依赖发布(Framework-Dependent)
dotnet publish -c Release -o ./publish

# 自包含发布(Self-Contained)
dotnet publish -c Release -r win-x64 --self-contained

# Native AOT
dotnet publish -c Release -r linux-x64 /p:PublishAot=true

不同发布方式的差异:

发布方式体积启动速度运行时依赖适用场景
框架依赖小(~100KB)需安装 .NET Runtime容器化、云原生
自包含大(~80MB)离线部署
Native AOT中(~10MB)极快函数计算、CLI 工具

5.4 引入 NuGet 包

dotnet add package Newtonsoft.Json
dotnet add package Serilog.Extensions.Hosting --version 8.0.0

Program.cs

using Newtonsoft.Json;

var person = new { Name = "Alice", Age = 30 };
string json = JsonConvert.SerializeObject(person);
Console.WriteLine(json);

var deserialized = JsonConvert.DeserializeObject<dynamic>(json);
Console.WriteLine($"Name: {deserialized.Name}, Age: {deserialized.Age}");

编译运行:

dotnet run

输出:

{"Name":"Alice","Age":30}
Name: Alice, Age: 30

5.5 多项目解决方案

mkdir Demo && cd Demo
dotnet new sln -n Demo
dotnet new console -o src/App -n App
dotnet new classlib -o src/Lib -n Lib
dotnet sln add src/App/App.csproj
dotnet sln add src/Lib/Lib.csproj
cd src/App && dotnet add reference ../Lib/Lib.csproj

Lib/Class1.cs(重命名为 Calculator.cs):

namespace Lib;

public static class Calculator
{
    public static int Add(int a, int b) => a + b;
    public static int Multiply(int a, int b) => a * b;
}

App/Program.cs

using Lib;

Console.WriteLine(Calculator.Add(3, 5));
Console.WriteLine(Calculator.Multiply(3, 5));

5.6 dotnet 命令速查表

# 项目创建
dotnet new console|classlib|web|webapi|mstest|xunit|sln|gitignore|globaljson

# 编译运行
dotnet build
dotnet run
dotnet publish

# 包管理
dotnet add package <name>
dotnet add reference <project>
dotnet remove package <name>
dotnet list package --outdated

# 测试
dotnet test
dotnet test --filter "FullyQualifiedName~CalculatorTests"
dotnet test --collect:"XPlat Code Coverage"

# 工具
dotnet tool install -g dotnet-format
dotnet tool install -g dotnet-ef
dotnet tool list -g
dotnet tool update -g dotnet-format

# 解决方案
dotnet sln add <project>
dotnet sln remove <project>
dotnet sln list

# EF Core
dotnet ef migrations add InitialCreate
dotnet ef database update
dotnet ef dbcontext scaffold "<connection>" Microsoft.EntityFrameworkCore.SqlServer

5.7 调试配置

5.7.1 VS Code launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": ".NET Core Launch (console)",
      "type": "coreclr",
      "request": "launch",
      "preLaunchTask": "build",
      "program": "${workspaceFolder}/bin/Debug/net9.0/${workspaceFolderBasename}.dll",
      "args": [],
      "cwd": "${workspaceFolder}",
      "stopAtEntry": false,
      "console": "internalConsole"
    },
    {
      "name": ".NET Core Attach",
      "type": "coreclr",
      "request": "attach",
      "processId": "${command:pickProcess}"
    }
  ]
}

5.7.2 VS Code tasks.json

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build",
      "command": "dotnet",
      "type": "process",
      "args": [
        "build",
        "${workspaceFolder}/${workspaceFolderBasename}.csproj",
        "/property:GenerateFullPaths=true",
        "/consoleloggerparameters:NoSummary"
      ],
      "problemMatcher": "$msCompile"
    }
  ]
}

5.8 完整示例:温度转换器

Program.cs

using System.Globalization;

namespace HelloFandex;

public static class Program
{
    public static int Main(string[] args)
    {
        if (args.Length == 0)
        {
            PrintUsage();
            return 1;
        }

        if (!double.TryParse(args[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var celsius))
        {
            Console.Error.WriteLine($"Invalid temperature: {args[0]}");
            return 2;
        }

        double fahrenheit = CelsiusToFahrenheit(celsius);
        Console.WriteLine($"{celsius:F2} °C = {fahrenheit:F2} °F");
        return 0;
    }

    /// <summary>
    /// 摄氏度转华氏度:F = C × 9/5 + 32
    /// </summary>
    public static double CelsiusToFahrenheit(double celsius) => celsius * 9.0 / 5.0 + 32.0;

    private static void PrintUsage()
    {
        Console.WriteLine("Usage: HelloFandex <celsius>");
        Console.WriteLine("Example: HelloFandex 36.5");
    }
}

编译与运行:

dotnet build
dotnet run -- 36.5
dotnet run --project HelloFandex.csproj -- 100

期望输出:

36.50 °C = 97.70 °F
100.00 °C = 212.00 °F

6. 对比分析

6.1 C# vs Java

维度C#Java
公司MicrosoftOracle(原 Sun)
首次发布20021995
编译目标CIL(CLR)Bytecode(JVM)
泛型实现真泛型(CLR 层)类型擦除(语法糖)
值类型struct(栈分配)仅 primitive
异步语法async/await(语言级)CompletableFuture + virtual thread(Java 21)
属性(Property)原生支持需 getter/setter 方法
事件(Event)原生支持需接口或监听器
LINQ语言集成Stream API(Java 8+)
运行时 GC分代 + 后台 + 服务端G1、ZGC、Shenandoah
AOTNative AOT(.NET 7+)GraalVM、Native Image
桌面WPF、WinForms、MAUIJavaFX
WebASP.NET CoreSpring Boot
ORMEF CoreHibernate、JPA
平台Win/Linux/macOS/iOS/Android同样跨平台

6.1.1 泛型差异深度对比

C#:

List<int> list = new() { 1, 2, 3 };
list.Add(4);
int sum = list.Sum();   // 无装箱

Java:

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
int sum = list.stream().mapToInt(Integer::intValue).sum();  // 拆箱开销

C# 的 List<int> 在底层是 int[],无装箱;Java 的 ArrayList<Integer> 底层是 Object[],存在装箱开销。

6.1.2 异步对比

C#:

public async Task<string> GetDataAsync(string url)
{
    using var client = new HttpClient();
    return await client.GetStringAsync(url);
}

Java(CompletableFuture):

public CompletableFuture<String> getDataAsync(String url) {
    return CompletableFuture.supplyAsync(() -> {
        try {
            return new HttpClient().send(
                HttpRequest.newBuilder(URI.create(url)).build(),
                BodyHandlers.ofString()
            ).body();
        } catch (Exception e) { throw new RuntimeException(e); }
    });
}

Java 21 Virtual Thread:

public String getData(String url) throws Exception {
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        return executor.submit(() -> new HttpClient().send(
            HttpRequest.newBuilder(URI.create(url)).build(),
            BodyHandlers.ofString()
        ).body()).get();
    }
}

C# 的 async/await 在编译期重写为状态机,无需操作系统线程;Java 的 Virtual Thread 类似,但需要 JVM 层支持。

6.2 C# vs TypeScript

维度C#TypeScript
类型系统静态、强静态、结构化、可选
运行时CLRV8 / Node.js
类型擦除是(编译为 JS)
装箱值类型引用上下文无(JS 全是对象)
泛型真泛型类型擦除(语法糖)
异步Task / ValueTaskPromise
元组元组类型 (int, string)元组类型 [number, string]
Union 类型无(C# 13 部分支持)string | number
反射完整有限(设计时 only)
装饰器特性(Attribute)装饰器(实验性)

6.3 C# vs Kotlin

维度C#Kotlin
平台.NETJVM、Android、JS、Native
空安全NRT(编译期警告)类型系统层(T?
数据类recorddata class
协程async/await + TaskCoroutine(suspend)
扩展方法全局静态全局静态
属性原生原生
默认参数支持支持
函数式LINQ、LambdaCollection API、Lambda
与 Java 互操作-完美

6.4 C# vs Go

维度C#Go
编译JIT + AOTAOT
启动速度慢(JIT 预热) / AOT 快极快
GC分代、并发并发标记清除
并发模型async/await + TaskGoroutine + Channel
类型系统复杂、面向对象简洁、结构化(结构体+接口)
错误处理异常多返回值 + error
泛型C# 2.0 起Go 1.18 起
包管理NuGetGo Modules

6.5 跨语言 Hello World 对比

C#:

Console.WriteLine("Hello, World!");

Java:

System.out.println("Hello, World!");

Kotlin:

println("Hello, World!")

Go:

package main
import "fmt"
func main() { fmt.Println("Hello, World!") }

TypeScript:

console.log("Hello, World!");

Python:

print("Hello, World!")

Rust:

fn main() { println!("Hello, World!"); }

C++:

#include <iostream>
int main() { std::cout << "Hello, World!" << std::endl; }

7. 常见陷阱与反模式

7.1 SDK 与 Runtime 版本不匹配

症状

It was not possible to find any compatible framework version
The framework 'Microsoft.NETCore.App', version '9.0.0' was not found.

原因:开发机安装的 SDK 版本与生产环境 Runtime 不一致。

对策

  • 生产环境始终使用 LTS(.NET 8)。
  • global.json 锁定 SDK 版本:
{
  "sdk": {
    "version": "9.0.100",
    "rollForward": "latestFeature"
  }
}

7.2 顶级语句滥用

反模式:将整个企业级应用写在 Program.cs 顶级语句中,文件超过 2000 行。

对策:顶级语句仅用于启动器,业务逻辑分层到独立项目 / 类中。

7.3 var 滥用

反模式

var result = DoSomething();   // result 是什么类型?
var x = GetX();

对策

  • 类型明显时使用 varvar list = new List<int>();
  • 类型不明显时显式声明:Person person = GetPerson();

7.4 异常吞咽

反模式

try { DoSomething(); }
catch (Exception) { /* 啥也不做 */ }

对策

  • 至少记录日志:catch (Exception e) { _logger.LogError(e, "Failed"); }
  • 区分可恢复与不可恢复异常。

7.5 同步阻塞异步方法

反模式

var result = GetAsync().Result;  // 死锁风险
var result = GetAsync().Wait();

对策:异步方法应 await,不要 .Result / .Wait()。详见《异步编程详解》。

7.6 装箱陷阱

反模式

ArrayList list = new();   // 非泛型,所有 int 装箱
list.Add(1);
list.Add(2);
int sum = 0;
foreach (int i in list) sum += i;

对策:始终使用泛型集合 List<int>

7.7 字符串拼接陷阱

反模式

string s = "";
for (int i = 0; i < 1000; i++) s += i;   // O(n²) 复杂度

对策:使用 StringBuilder

var sb = new StringBuilder();
for (int i = 0; i < 1000; i++) sb.Append(i);
string s = sb.ToString();

7.8 配置文件未拷贝到输出目录

症状:发布后找不到 appsettings.json

对策:在 .csproj 中:

<ItemGroup>
  <None Update="appsettings.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

7.9 NRT 与可空警告

症状:开启 NRT 后大量警告 CS8602: Dereference of a possibly null reference

对策

  • 不要粗暴 !(null-forgiving)压制。
  • 重新审视 null 来源,使用 ???.if (obj is not null)
  • 对外部输入显式校验:ArgumentException.ThrowIfNull(arg)

7.10 .NET Standard 滥用

反模式:所有新类库都建为 .NET Standard 2.0

对策

  • 若库仅供 .NET 6/8 项目使用,目标框架设为 net8.0
  • .NET Standard 2.0 仅用于需要兼容 .NET Framework 4.6.1+ 与旧 Mono 的场景。

8. 工程实践与最佳实践

8.1 项目结构约定

推荐的解决方案目录结构:

MyApp/
├── src/
│   ├── MyApp.Api/              # API 入口
│   ├── MyApp.Application/      # 应用服务层
│   ├── MyApp.Domain/           # 领域模型
│   ├── MyApp.Infrastructure/   # 基础设施
│   └── MyApp.Tests/            # 测试
├── tests/
│   ├── MyApp.UnitTests/
│   └── MyApp.IntegrationTests/
├── docs/
├── scripts/
├── .editorconfig
├── .gitignore
├── Directory.Build.props
├── Directory.Packages.props
├── global.json
├── nuget.config
└── MySolution.sln

8.2 Directory.Build.props

集中配置所有项目的公共属性:

<Project>

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <NoWarn>$(NoWarn);CS1591</NoWarn>
    <Authors>fanquanpp</Authors>
    <Company>FANDEX</Company>
    <Product>FANDEX</Product>
    <Copyright>Copyright (c) 2026 FANDEX</Copyright>
    <Version>1.0.0</Version>
  </PropertyGroup>

</Project>

8.3 Directory.Packages.props

集中管理 NuGet 版本(Central Package Management):

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="Serilog" Version="4.0.0" />
    <PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0" />
    <PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
    <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
    <PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.0" />
    <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" />
  </ItemGroup>
</Project>

8.4 global.json

{
  "sdk": {
    "version": "9.0.100",
    "rollForward": "latestFeature",
    "allowPrerelease": false
  }
}

8.5 .editorconfig

# Top-level EditorConfig
root = true

# All files
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

# C# files
[*.cs]
dotnet_sort_system_directives_first = true
csharp_new_line_before_open_brace = all
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_elsewhere = true:suggestion

# Naming conventions
dotnet_naming_rule.public_members_must_be_capitalized.symbols = public_symbols
dotnet_naming_rule.public_members_must_be_capitalized.style = pascal_case_style
dotnet_naming_rule.public_members_must_be_capitalized.severity = warning

dotnet_naming_symbols.public_symbols.applicable_kinds = property,method,field,event,class,struct,interface,enum
dotnet_naming_symbols.public_symbols.applicable_accessibilities = public

dotnet_naming_style.pascal_case_style.capitalization = pascal_case

8.6 .gitignore

使用 dotnet new gitignore 生成默认模板,重点忽略:

bin/
obj/
*.user
*.suo
.vs/
.idea/
*.DS_Store
publish/
TestResults/

8.7 代码格式化

# 安装 dotnet-format
dotnet tool install -g dotnet-format

# 格式化整个解决方案
dotnet format MySolution.sln

# 仅检查,不修改
dotnet format --verify-no-changes

8.8 项目模板定制

# 创建模板包
dotnet new install Microsoft.DotNet.Common.ProjectTemplates.9.0
dotnet new install Microsoft.AspNetCore.Templates
dotnet new install Microsoft.Maui.Templates
dotnet new list

8.9 单元测试

dotnet new xunit -o tests/MyApp.Tests
dotnet add tests/MyApp.Tests reference src/MyApp/MyApp.csproj

CalculatorTests.cs

using HelloFandex;
using Xunit;

namespace MyApp.Tests;

public class CalculatorTests
{
    [Theory]
    [InlineData(0, 32)]
    [InlineData(100, 212)]
    [InlineData(-40, -40)]
    public void CelsiusToFahrenheit_Returns_Correct_Value(double c, double expected)
    {
        double actual = Program.CelsiusToFahrenheit(c);
        Assert.Equal(expected, actual, precision: 2);
    }
}

8.10 CI/CD(GitHub Actions)

.github/workflows/ci.yml

name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 9.0.x
      - run: dotnet restore
      - run: dotnet build --no-restore --configuration Release
      - run: dotnet test --no-build --configuration Release --verbosity normal
      - run: dotnet publish --no-build --configuration Release -o ./publish
      - uses: actions/upload-artifact@v4
        with:
          name: publish
          path: ./publish

8.11 Docker 化

Dockerfile

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY ["HelloFandex.csproj", "./"]
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:9.0-noble-chiseled AS final
WORKDIR /app
COPY --from=build /app/publish ./
ENTRYPOINT ["dotnet", "HelloFandex.dll"]

构建与运行:

docker build -t hello-fandex:1.0.0 .
docker run --rm hello-fandex:1.0.0 36.5

8.12 日志与可观测性

推荐使用 Serilog 或内置 Microsoft.Extensions.Logging

using Microsoft.Extensions.Logging;

var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddConsole();
    builder.AddDebug();
});
var logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("Application starting");
logger.LogWarning("This is a warning");
logger.LogError("This is an error");

OpenTelemetry(.NET 9 内置支持):

using OpenTelemetry.Trace;
using OpenTelemetry.Metrics;

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddOpenTelemetry()
    .WithTracing(tp => tp
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddOtlpExporter())
    .WithMetrics(mp => mp
        .AddAspNetCoreInstrumentation()
        .AddOtlpExporter());

await builder.Build().RunAsync();

9. 案例研究

9.1 案例:ASP.NET Core 最小 API

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI();

app.MapGet("/hello", () => new { Message = "Hello, FANDEX!" });
app.MapGet("/c2f/{celsius:double}", (double celsius) =>
{
    double f = celsius * 9.0 / 5.0 + 32.0;
    return new { Celsius = celsius, Fahrenheit = f };
});

app.Run();

创建命令:

dotnet new web -n WebDemo
cd WebDemo
dotnet add package Swashbuckle.AspNetCore
dotnet run

打开 http://localhost:5000/swagger 查看 API 文档。

9.2 案例:Unity 中的 C# 脚本

Unity 使用 C# 作为脚本语言,但运行时为 Mono / IL2CPP 而非完整 .NET Runtime。一个简单脚本:

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float speed = 5f;

    private void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        Vector3 movement = new Vector3(horizontal, 0f, vertical);
        transform.Translate(movement * speed * Time.deltaTime);
    }
}

Unity 工程通常使用 .NET Standard 2.1 兼容子集,最新 Unity 2023+ 支持 .NET 8 的子集。

9.3 案例:MAUI 跨平台 App

MauiProgram.cs

using Microsoft.Maui.Controls.Hosting;

namespace FandexMaui;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        return builder.Build();
    }
}

MainPage.xaml

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="FandexMaui.MainPage">
    <StackLayout Padding="30">
        <Label Text="Hello, FANDEX MAUI!" FontSize="32" HorizontalOptions="Center" />
        <Button Text="Click Me" Clicked="OnButtonClicked" />
    </StackLayout>
</ContentPage>

9.4 案例:WPF 桌面应用

App.xaml.cs

using System.Windows;

namespace FandexWpf;

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var window = new MainWindow();
        window.Show();
    }
}

MainWindow.xaml

<Window x:Class="FandexWpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="FANDEX" Height="450" Width="800">
    <Grid>
        <Button Content="Hello" Click="Button_Click" />
    </Grid>
</Window>

9.5 案例:Blazor WebAssembly

Program.cs

using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

await builder.Build().RunAsync();

Pages/Counter.razor

@page "/counter"

<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount() => currentCount++;
}

9.6 案例:FANDEX 项目目录结构

FANDEX-Web 项目本身使用 Astro 框架,但本系列文档(C# 教程)涉及的所有示例项目结构如下:

FANDEX-csharp-samples/
├── 01-hello/
├── 02-async/
├── 03-linq/
├── 04-aspnetcore/
├── 05-efcore/
├── 06-maui/
├── 07-blazor/
├── 08-unity/
├── 09-source-generators/
└── README.md

读者可在每篇文档末尾的”延伸阅读”找到对应的示例仓库链接。

9.7 案例:从 .NET Framework 迁移到 .NET 9

初始状态

  • .NET Framework 4.8 + ASP.NET MVC 5 + EF6。
  • 部署在 Windows Server + IIS。

迁移步骤

  1. 目标框架兼容性分析:使用 .NET Portability Analyzer 工具。
  2. 替换不兼容 APIAppDomainAssemblyLoadContextSystem.Web.HttpContextMicrosoft.AspNetCore.Http.HttpContext
  3. EF6 → EF Core:使用 EF Core Power Tools 逆向工程生成 DbContext。
  4. 迁移 ASP.NET MVC → ASP.NET Core MVC:Controller 与 View 大部分可平移。
  5. 配置文件web.configappsettings.json + IConfiguration
  6. DIGlobal.asax 中的初始化 → Program.cs + IServiceCollection
  7. 测试:xUnit + 测试容器化。
  8. 部署:IIS → Kestrel + Nginx 反向代理,或 Docker。

迁移收益:

  • Linux 容器化部署,云成本降低 60%。
  • 启动速度提升 3 倍(Kestrel 比 IIS pipeline 简化)。
  • 内存占用降低 40%。

10. 习题与思考题

10.1 选择题(基础)

Q1:以下哪个不是 .NET 的核心组成?

  • A. CLR
  • B. BCL
  • C. JVM
  • D. AppModel

答案:C。JVM 是 Java 的虚拟机。

Q2:C# 由谁设计?

  • A. James Gosling
  • B. Anders Hejlsberg
  • C. Bjarne Stroustrup
  • D. Guido van Rossum

答案:B。Anders Hejlsberg 也是 Turbo Pascal 与 TypeScript 的设计者。

Q3:以下哪个版本引入了 async/await?

  • A. C# 1.0
  • B. C# 3.0
  • C. C# 5.0
  • D. C# 7.0

答案:C。C# 5.0(2012)引入 async/await。

Q4:以下哪个 C# 版本引入了顶级语句?

  • A. C# 8.0
  • B. C# 9.0
  • C. C# 10.0
  • D. C# 11.0

答案:B。C# 9.0(2020)。

Q5:Native AOT 在哪个 .NET 版本正式可用?

  • A. .NET 5
  • B. .NET 6
  • C. .NET 7
  • D. .NET 8

答案:D。.NET 8 正式支持 Native AOT 发布。

10.2 选择题(进阶)

Q6:以下关于 .NET GC 说法正确的是?

  • A. 第 0 代回收时间最长
  • B. LOH 用于存储大于 85,000 字节的对象
  • C. Server GC 适合客户端应用
  • D. GC 始终是停止世界的

答案:B。LOH 阈值约 85KB;第 0 代回收最快;Server GC 适合服务端;.NET GC 支持后台并发回收。

Q7:以下关于 NuGet 的说法错误的是?

  • A. NuGet 是 .NET 的包管理器
  • B. dotnet add package 会自动还原
  • C. NuGet 包始终包含源代码
  • D. NuGet 支持 private feed

答案:C。NuGet 包通常只包含编译后的 DLL,不含源代码。

Q8:以下关于 JIT 与 AOT 说法正确的是?

  • A. JIT 启动速度比 AOT 快
  • B. AOT 不能进行任何运行时优化
  • C. JIT 可基于运行时 profile 进行优化
  • D. AOT 体积一定大于 JIT

答案:C。JIT 拥有运行时信息,可进行 profile-guided optimization。

10.3 简答题

Q9:简述 C# 与 Java 在泛型实现上的差异。

参考答案

C# 在 CLR 层实现真泛型,每个值类型泛型实例化(如 List<int>)在运行时是独立的类型,底层为 int[],无装箱开销。引用类型泛型共享代码(如 List<string>List<object> 共享同一份 JIT 代码),但类型安全由 CLR 保证。

Java 通过类型擦除(Type Erasure)实现泛型,编译后 List<Integer>List<String> 都是 List,底层为 Object[]。这导致:

  1. 值类型存在装箱开销。
  2. 运行时无法获取泛型参数类型(List<Integer>.class 不存在)。
  3. 不能 new T()。
  4. 不能创建泛型数组。

C# 在这些场景下表现更好。

Q10:为什么 .NET Core 跨平台而 .NET Framework 不能?

参考答案

.NET Framework 设计于 2000 年代,深度耦合 Windows:

  1. BCL 依赖 Win32 APISystem.IOSystem.DrawingSystem.Diagnostics.Process 等内部调用 Win32 函数。
  2. AppDomain:基于 Windows 进程模型。
  3. WCF / Web Forms / WPF:依赖 IIS、COM+、DirectX。
  4. GC:早期 Server GC 专为 Windows 设计。
  5. RegistryMicrosoft.Win32.Registry 直接调用 Windows 注册表。

.NET Core 重写了这些子系统:

  1. PAL(Platform Adaptation Layer):抽象 OS 差异。
  2. BCL 重写:基于 System.IO.FileSystemSystem.Net.Http 等跨平台实现。
  3. Kestrel:自研跨平台 Web 服务器。
  4. Native P/Invoke:每个平台 P/Invoke 不同的系统库。

10.4 编程题

Q11:编写一个 C# 控制台程序,接收命令行参数 <n>,输出斐波那契数列前 n 项。

参考答案

if (args.Length == 0 || !int.TryParse(args[0], out int n) || n <= 0)
{
    Console.Error.WriteLine("Usage: Fib <n>");
    return 1;
}

long a = 0, b = 1;
for (int i = 0; i < n; i++)
{
    Console.Write($"{a} ");
    (a, b) = (b, a + b);
}
Console.WriteLine();
return 0;

Q12:编写一个程序,将给定目录下所有 .cs 文件按行数从多到少排序输出。

参考答案

using System.IO;

if (args.Length == 0 || !Directory.Exists(args[0]))
{
    Console.Error.WriteLine("Usage: LineCount <directory>");
    return 1;
}

var files = Directory.EnumerateFiles(args[0], "*.cs", SearchOption.AllDirectories)
    .Select(path => new { Path = path, Lines = File.ReadAllLines(path).Length })
    .OrderByDescending(f => f.Lines);

foreach (var f in files)
{
    Console.WriteLine($"{f.Lines,6} {f.Path}");
}

return 0;

Q13:编写一个程序,使用 HttpClient 异步下载指定 URL 的内容,统计下载耗时与字节数。

参考答案

using System.Diagnostics;

if (args.Length == 0)
{
    Console.Error.WriteLine("Usage: Download <url>");
    return 1;
}

using var client = new HttpClient();
var sw = Stopwatch.StartNew();
byte[] data = await client.GetByteArrayAsync(args[0]);
sw.Stop();
Console.WriteLine($"Downloaded {data.Length:N0} bytes in {sw.ElapsedMilliseconds} ms");

10.5 思考题

Q14:为什么微软要放弃 .NET Framework,重写 .NET Core?请从技术、商业、生态三个维度分析。

参考答案要点

  • 技术:跨平台需求(Linux 容器化)、性能瓶颈(Framework 太重)、模块化(NuGet 化)、新场景(云原生、IoT)。
  • 商业:与 Java 在企业市场(尤其 Linux 服务器)竞争;Azure 云服务需要轻量运行时。
  • 生态:开源社区贡献需要更友好的开发模型;F#、VB.NET、IronPython 等共享运行时。

Q15:如果让你设计 C# 的下一项新特性,你会提议什么?请说明动机与潜在风险。

参考答案示例

提议:协变返回类型扩展 —— 允许方法重写返回更窄的具体类型,包括值类型。当前 C# 9 支持引用类型协变返回,但不支持值类型。

动机:领域驱动设计中,基类 Repository<T>Find(id) 返回 T,子类 UserRepository 想返回 User,目前无法做到零装箱。

风险:可能导致 ABI 不兼容;需评估 CLR 是否需要扩展。

10.6 综合应用题

Q16:你要为公司搭建一个内部微服务,技术选型在 .NET 8 与 Spring Boot 3 之间犹豫。请列出选型要点,并给出推荐。

参考答案要点

维度.NET 8Spring Boot 3
性能Kestrel 吞吐量略高Tomcat 性能优秀
内存容器内存占用较低JVM 内存管理灵活
团队熟悉度取决于团队取决于团队
生态NuGet 包相对集中Maven 包海量
监控OpenTelemetry 内置Spring Cloud 全家桶
部署Docker 镜像较小Docker 镜像较大
启动速度Native AOT 极快GraalVM Native Image

推荐:若团队以 C# 为主且追求启动速度(如 AWS Lambda 函数计算),选 .NET 8 + Native AOT。若团队以 Java 为主且需要复杂业务集成(如大型金融系统),选 Spring Boot 3。


11. 参考文献

以下参考文献遵循 ACM Reference Format

  1. Hejlsberg, A., Torgersen, M., Wiltamuth, S., and Golde, P. 2010. The C# Programming Language (4th ed.). Addison-Wesley Professional, Boston, MA, USA.

  2. Microsoft Corporation. 2024. C# Language Specification. Retrieved July 21, 2026 from https://learn.microsoft.com/dotnet/csharp/language-reference/

  3. Microsoft Corporation. 2024. .NET Documentation. Retrieved July 21, 2026 from https://learn.microsoft.com/dotnet/

  4. Wagner, B. 2017. Effective C# (Covers C# 7.0) (3rd ed.). Addison-Wesley Professional, Boston, MA, USA.

  5. Wagner, B. 2018. More Effective C#: 50 Specific Ways to Improve Your C# (2nd ed.). Addison-Wesley Professional, Boston, MA, USA.

  6. Skeet, J. 2019. C# in Depth (4th ed.). Manning Publications, Shelter Island, NY, USA.

  7. Abramson, N., Torgersen, M., Huguet, P., and Wiltamuth, S. 2020. C# 9.0 on the record. Microsoft Build. Retrieved July 21, 2026 from https://devblogs.microsoft.com/dotnet/welcome-to-csharp-9/

  8. Torgersen, M. 2023. Welcome to C# 12. Microsoft .NET Blog. Retrieved July 21, 2026 from https://devblogs.microsoft.com/dotnet/welcome-to-csharp-12/

  9. Kennedy, A., and Syme, D. 2001. Design and Implementation of Generics for the .NET Common Language Runtime. Journal of Functional Programming 11, 6 (Nov. 2001), 605-628. DOI: https://doi.org/10.1017/S0956796801004256

  10. Bierman, G. M., Parkinson, M. J., and Pitts, A. M. 2003. The effect of a null-able type on the Java programming language. In ECOOP 2003 – Object-Oriented Programming, Springer, Berlin, Germany, 30-45.

  11. Duffy, J. 2010. Concurrent Programming on Windows. Addison-Wesley Professional, Boston, MA, USA.

  12. Richter, J. 2012. CLR via C# (4th ed.). Microsoft Press, Redmond, WA, USA.

  13. Stovell, D., and Park, S. 2023. .NET Microservices: Architecture for Containerized .NET Applications (2nd ed.). Microsoft Developer Division. Retrieved July 21, 2026 from https://learn.microsoft.com/dotnet/architecture/microservices/

  14. Solomon, A., and Rousos, D. 2023. .NET Microservices: Architecture for Containerized .NET Applications. Microsoft Press, Redmond, WA, USA.

  15. Miller, J., and Ragsdale, S. 2006. Common Language Infrastructure (CLI) Standard (ECMA-335). ECMA International, Geneva, Switzerland.


12. 延伸阅读

12.1 官方文档

12.2 系列内交叉引用

继续学习 C# 的下一站:

12.3 进阶书籍

  • Andrew Troelsen, Phil Japkiewicz. Pro C# 10 with .NET 6 (Apress, 2022).
  • Mark Michaelis. Essential C# 8.0 (Addison-Wesley, 2020).
  • Joseph Albahari. C# 12 in a Nutshell (O’Reilly, 2024).
  • Oren Eini. Pro .NET Memory Management and Pointers (Apress, 2022).
  • Stephen Cleary. Concurrency in C# Cookbook (O’Reilly, 2019).

12.4 社区资源

12.5 视频资源

12.6 工具与扩展

  • dotnet-format:代码格式化工具
  • dotnet-ef:EF Core CLI 工具
  • dotnet-counters:实时性能计数器
  • dotnet-dump:堆转储分析
  • dotnet-trace:性能追踪
  • PerfView:免费性能分析工具
  • dotMemory:JetBrains 内存分析(付费)
  • dotTrace:JetBrains CPU 分析(付费)
  • ILSpy / dnSpy:反编译工具

12.7 习题答案汇总

题号答案
Q1C
Q2B
Q3C
Q4B
Q5D
Q6B
Q7C
Q8C
Q9-Q16见各题参考答案

结语

至此,我们对 C# 语言与 .NET 平台建立了宏观认知。从 Anders Hejlsberg 在 2000 年的设计哲学,到 2024 年 .NET 9 的 Native AOT、HybridCache、动态 PGO,C# 已经从一个 Windows-only 语言演化为覆盖云、桌面、移动、游戏、IoT 的全栈平台。

下一步,请进入 基础语法,开始动手编写真正的 C# 代码。记住:

  • 不要满足于”能跑”:理解每行代码背后发生了什么。
  • 不要怕出错:错误是最好的老师,善用异常堆栈与调试器。
  • 不要孤立学习:每一篇文档都关联着上下游,交叉阅读能加深理解。

愿你在 FANDEX 的 C# 之旅中,既能掌握工程实战,也能体味语言设计之美。


本文由 FANDEX 团队编写,最后更新于 2026-07-21。若有疑问或建议,欢迎在项目仓库提交 Issue。

返回入门指南