C# 概述与环境配置
C# 语言概述、.NET 生态、.NET 8/9 新特性、环境搭建与 Hello World
C# 概述与环境配置
前置知识
- C# 是什么:.NET 世界的通用语言:建议先完成前一篇的学习
学习目标
- 掌握「1. 历史动机与演化」的核心机制、典型用法与常见陷阱
- 掌握「2. 形式化定义」的核心机制、典型用法与常见陷阱
- 掌握「3. 理论推导与证明」的核心机制、典型用法与常见陷阱
- 掌握「4. 代码示例」的核心机制、典型用法与常见陷阱
- 掌握「5. 对比分析」的核心机制、典型用法与常见陷阱
本文是 FANDEX C# 系列的开篇。我们将从语言的诞生背景、设计哲学、运行平台、生态构成,到本地环境的搭建与第一个可运行程序,全方位带你建立对 C# 与 .NET 的宏观认知。内容对标 MIT 6.0001、Stanford CS106X、CMU 15-110 等海外名校入门课程的教学严谨度,同时融入企业级工程实战经验,支持 0 基础自学。
本节为增量补充,帮助你选择 .NET 与 C# 版本。
- .NET:10 为当前 LTS(2025-11 发布,支持至 2028-11),随附 C# 14;.NET 11 计划 2026-11 发布。企业新项目推荐 .NET 10 LTS。
- 工具链:
dotnetCLI 是统一入口;编辑器可选 Visual Studio 2026、VS Code + C# Dev Kit 或 Rider。 - 平台:ASP.NET Core 用于 Web,EF Core 用于数据访问,MAUI 用于跨平台客户端。
1. 历史动机与演化
理解一门语言的现在,必须先理解它走过的路。C# 的演化是一部”在工程实用主义与语言创新之间不断平衡”的历史。
1.1 史前背景:COM 时代的痛点
在 .NET 诞生之前(1990 年代),微软生态主流是 COM(Component Object Model)与 MFC(Microsoft Foundation Classes)。开发者面临的核心痛点包括:
- 语言割裂:C++、VB6、Delphi 各自为政,组件互操作需通过 COM 的
IUnknown、IDispatch、BSTR、SAFEARRAY等繁杂 ABI,易错且低效。 - 内存管理混乱:C++ 需手工管理
new/delete,VB6 采用引用计数(Reference Counting),两者协作时易出现循环引用导致的内存泄漏。 - 跨语言调用成本高:C++ 写的 COM 组件被 VB 调用时需要 IDL(Interface Definition Language)转译,开发体验差。
- Java 的崛起:1995 年 Sun 推出 Java,凭借”Write Once, Run Anywhere”与垃圾回收(GC)抢占企业市场,对微软构成战略威胁。
1.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 隔离。
1.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):在委托层面初步支持。
1.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; }。
1.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 基础。
1.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。
1.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" }。
1.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。
1.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 共享问题。
1.10 .NET Core 3.0 / 3.1(2019)
- Windows 桌面支持回归:WPF、WinForms 在 .NET Core 3.0 复活。
- 默认可空引用类型(NRT):C# 8.0 引入,编译期 null 安全。
- 异步流(IAsyncEnumerable
) :await foreach。 - 索引与范围(Index / Range):
arr[^1]、arr[1..3]。 - switch 表达式:模式匹配的”函数式”形态。
- 默认接口方法:接口可包含实现。
- unmanaged 泛型约束:
where T : unmanaged。
1.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; },对象初始化器阶段可写、之后只读。 - 模式匹配增强:
and、or、not模式组合。 - 目标类型 new:
Person p = new();。 - 协变返回类型:子类 override 可返回更窄类型。
- *函数指针(delegate)**:非托管调用零开销。
- 源生成器(Source Generators):编译期代码生成,替代反射。
1.12 .NET 6(2021 LTS)与 C# 10.0
- 全局 using:
global 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)。
1.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 编译。
1.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 正式可用:发布时无运行时依赖。
1.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%。
1.16 演化时间线总览
| 年份 | .NET 版本 | C# 版本 | 标志性特性 | LTS/STS |
|---|---|---|---|---|
| 2002 | .NET Framework 1.0 | 1.0 | CLR、C# 诞生 | - |
| 2005 | .NET Framework 2.0 | 2.0 | 泛型、可空类型 | - |
| 2007 | .NET Framework 3.5 | 3.0 | LINQ、Lambda | - |
| 2010 | .NET Framework 4.0 | 4.0 | dynamic、TPL | - |
| 2012 | .NET Framework 4.5 | 5.0 | async/await | - |
| 2015 | .NET Framework 4.6 | 6.0 | 字符串插值、null 条件 | - |
| 2017 | .NET Framework 4.7 / Core 2.0 | 7.x | 元组、模式匹配、Span | - |
| 2019 | .NET Core 3.1 | 8.0 | NRT、异步流 | LTS |
| 2020 | .NET 5 | 9.0 | record、顶级语句 | STS |
| 2021 | .NET 6 | 10.0 | MAUI、最小 API | LTS |
| 2022 | .NET 7 | 11.0 | 泛型数学、原始字符串 | STS |
| 2023 | .NET 8 | 12.0 | 主构造、Native AOT | LTS |
| 2024 | .NET 9 | 13.0 | HybridCache、PGO | STS |
1.17 设计哲学小结
C# 的演化贯穿三大原则:
- 实用主义:每项新特性都源于真实工程痛点,而非纯学术理想。
- 类型安全优先:通过编译期检查(如 NRT、模式匹配)消灭运行时错误。
- 性能不退步:任何抽象都可在性能关键场景被”穿透”(如
Span<T>、ref struct、ValueTask、Unsafe)。
理解这三点,能帮你预测未来 C# 演化方向。
2. 形式化定义
2.1 C# 语言的形式化定义
C# 是一种静态类型、强类型、面向对象、多范式、托管执行的编程语言。下面从形式化角度拆解这些性质。
2.1.1 类型系统
设 为类型集合, 为类型环境(typing context),表达式 的类型推断关系记为 。C# 的类型规则可形式化为:
其中 表示类型相容(含隐式转换)。
C# 类型系统在 Hindley-Milner 之上做了若干扩展:
- 子类型(Subtyping): 表示 是 的子类型。
- 协变(Covariance):若
IEnumerable<out T>,则 。 - 逆变(Contravariance):若
Action<in T>,则 。 - 可空性(Nullability):引用类型 拆为 (不可空)与 (可空)。
2.1.2 语法与文法
C# 文法以 LL(k) 与 LR 混合方式解析。其文法可表示为:
详细文法见 C# Language Specification(Dotnet GitHub 仓库 dotnet/csharpstandard)。
2.2 .NET 平台的形式化定义
.NET 平台可形式化为三元组:
其中:
- CLR(Common Language Runtime):执行引擎,提供 GC、JIT、类型安全、异常处理等运行时服务。
- BCL(Base Class Library):基础类库,包含集合、IO、网络、LINQ、反射等。
- AppModel(Application Model):应用模型,如 ASP.NET Core、EF Core、MAUI、WPF、WinForms。
2.3 程序执行流程的形式化
C# 源代码的执行流程可形式化为映射链:
详细步骤:
- 源代码:
.cs文件,人类可读。 - 词法分析 → 语法分析:生成抽象语法树(AST)。
- 语义分析:类型检查、可空性分析、特性应用。
- CIL 生成:输出
.dll/.exe,内容为 Common Intermediate Language(字节码)。 - 类加载:CLR 加载程序集,验证类型安全。
- JIT 编译:方法首次调用时,JIT 将 CIL 编译为本地机器码。
- 执行:CPU 执行机器码,GC 在后台管理内存。
2.4 内存模型形式化
.NET 的托管堆(Managed Heap)可形式化为:
其中 为地址, 为类型, 为所属 GC 代(Generation 0/1/2/LOH), 为大小。
GC 的回收策略可表示为:
回收后保留的对象晋升一代:
2.5 异步执行模型形式化
async/await 的状态机重写可形式化为变换 :
其中 是一个有限状态机 :
- :状态集合,每个
await点对应一个状态。 - :初始状态。
- :输入符号(awaitable 完成事件)。
- :状态转移函数。
- :终止状态集合。
详细推导见本系列《async-await 状态机》一文。
3. 理论推导与证明
3.1 类型安全性证明(Sketch)
C# 类型系统满足 Progress 与 Preservation 两大性质(受 Erasure 影响,对泛型稍作调整)。
定理 4.1(Progress):对于良型表达式 , 要么是一个值,要么存在 使得 。
证明(Sketch):对 的结构归纳。
- 若 是值(变量、字面量),成立。
- 若 ,由归纳 要么是值要么可归约;若 可归约,则 ;若 是值,同理处理 ;若两者都是值,依据算术运算法则可归约。
定理 4.2(Preservation):若 且 ,则 。
证明(Sketch):对归约步骤分类讨论。
- 算术:,归约为 ( 的算术结果), 成立。
- 函数调用:,由代入引理 。
3.2 协变/逆变安全性证明
定理 4.3(协变安全):若 interface I<out T>,且 ,则 I<T> <: I<U> 类型安全。
证明:协变要求 T 在 I 中只出现在输出位置(返回类型)。设 I<T> 暴露方法 T M(),对 I<T> 实例 o 调用 o.M() 返回 实例。若将 o 视为 I<U>,调用 o.M() 期望返回 。由 ,返回的 自动是 ,类型安全成立。
定理 4.4(逆变安全):若 interface I<in T>,且 ,则 I<U> <: I<T> 类型安全。
证明:逆变要求 T 只出现在输入位置。设 I<T> 暴露 void M(T x),对 I<T> 实例 o 调用 o.M(x) 要求 。若将 o 视为 I<U>(即接受 U 类型参数),调用 o.M(y) 其中 。由于 o 实际是 I<T> 实例,要求 ,而 意味着 实例不一定是 ,这看似不安全——但逆变的方向相反:I<U> <: I<T> 意味着 I<U> 可用作 I<T>。此时调用 o.M(x) 其中 ,由 , 也是 ,传给 I<U> 的 M(U) 安全。证毕。
3.3 GC 暂停时间复杂度
命题 4.5:.NET 的分代 GC 第 代回收时间 与该代对象数量 近似成正比,即 。
推导:分代 GC 采用标记-压缩(Mark-Compact)或标记-复制(Mark-Copy)算法。
- 标记阶段:从 GC Root 出发,BFS 遍历所有可达对象,复杂度 , 为对象引用边数。
- 压缩/复制阶段:遍历存活对象,更新引用,复杂度 。
由于第 0 代存活率低(多数对象短命),,故 极小。第 2 代(Gen 2)回收涉及全堆扫描, 较大。
3.4 JIT 编译优化
命题 4.6:JIT 编译产生的代码性能 满足 ,其中 为预热开销。
直觉:JIT 拥有 AOT 不具备的运行时信息:
- 类型 profile:哪些虚方法实际只有一个实现,可去虚化(Devirtualization)。
- 分支 profile:哪些分支几乎不执行,可优化布局。
- 内联:跨程序集边界的内联由 JIT 决定。
Profile-Guided Optimization(PGO)在 .NET 6+ 中将 JIT 优化推向新高度。.NET 9 的动态 PGO 进一步使 的场景比例提升。
3.5 Roslyn 编译流水线
Roslyn 编译器流水线可形式化为:
每一阶段都暴露为公开 API(Microsoft.CodeAnalysis.CSharp),使源生成器(Source Generators)、分析器(Analyzers)、代码修复(Code Fixes)成为可能。
4. 代码示例
4.1 安装 .NET SDK
4.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
4.1.2 macOS 安装
# Homebrew
brew install --cask dotnet-sdk
4.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
4.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]
4.2 第一个控制台应用
4.2.1 创建项目
mkdir HelloFandex && cd HelloFandex
dotnet new console -n HelloFandex -o .
dotnet run
dotnet new console 生成如下结构:
flowchart TD
T0["HelloFandex/"]
T1["HelloFandex.csproj"]
T2["Program.cs"]
T3["obj/"]
T4["HelloFandex.csproj.nuget.g.props"]
T0 --> T1
T0 --> T2
T0 --> T3
T3 --> T4
4.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!");
}
}
4.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 任务 |
OutputType | Exe 表示可执行;Library 表示类库 |
TargetFramework | 目标框架:net9.0、net8.0、netstandard2.0 |
ImplicitUsings | 是否自动 using 常用命名空间 |
Nullable | 启用可空引用类型检查 |
4.3 编译与运行机制
4.3.1 编译
dotnet build
执行流程:
- Restore:解析 NuGet 引用,写入
obj/project.assets.json。 - GenerateAssemblyInfo:生成
AssemblyVersion等元数据。 - Csc:调用 Roslyn 编译器,输出 CIL 到
bin/Debug/net9.0/HelloFandex.dll。 - CopyFilesMarkedCopyLocal:拷贝 NuGet 依赖到输出目录。
4.3.2 运行
dotnet run
dotnet run 等价于 dotnet build && dotnet <assembly.dll>,是开发期最常用命令。
4.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 工具 |
4.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
4.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));
4.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
4.7 调试配置
4.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}"
}
]
}
4.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"
}
]
}
4.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
5. 对比分析
5.1 C# vs Java
| 维度 | C# | Java |
|---|---|---|
| 公司 | Microsoft | Oracle(原 Sun) |
| 首次发布 | 2002 | 1995 |
| 编译目标 | 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 |
| AOT | Native AOT(.NET 7+) | GraalVM、Native Image |
| 桌面 | WPF、WinForms、MAUI | JavaFX |
| Web | ASP.NET Core | Spring Boot |
| ORM | EF Core | Hibernate、JPA |
| 平台 | Win/Linux/macOS/iOS/Android | 同样跨平台 |
5.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[],存在装箱开销。
5.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 层支持。
5.2 C# vs TypeScript
| 维度 | C# | TypeScript |
|---|---|---|
| 类型系统 | 静态、强 | 静态、结构化、可选 |
| 运行时 | CLR | V8 / Node.js |
| 类型擦除 | 否 | 是(编译为 JS) |
| 装箱 | 值类型引用上下文 | 无(JS 全是对象) |
| 泛型 | 真泛型 | 类型擦除(语法糖) |
| 异步 | Task / ValueTask | Promise |
| 元组 | 元组类型 (int, string) | 元组类型 [number, string] |
| Union 类型 | 无(C# 13 部分支持) | string | number |
| 反射 | 完整 | 有限(设计时 only) |
| 装饰器 | 特性(Attribute) | 装饰器(实验性) |
5.3 C# vs Kotlin
| 维度 | C# | Kotlin |
|---|---|---|
| 平台 | .NET | JVM、Android、JS、Native |
| 空安全 | NRT(编译期警告) | 类型系统层(T?) |
| 数据类 | record | data class |
| 协程 | async/await + Task | Coroutine(suspend) |
| 扩展方法 | 全局静态 | 全局静态 |
| 属性 | 原生 | 原生 |
| 默认参数 | 支持 | 支持 |
| 函数式 | LINQ、Lambda | Collection API、Lambda |
| 与 Java 互操作 | - | 完美 |
5.4 C# vs Go
| 维度 | C# | Go |
|---|---|---|
| 编译 | JIT + AOT | AOT |
| 启动速度 | 慢(JIT 预热) / AOT 快 | 极快 |
| GC | 分代、并发 | 并发标记清除 |
| 并发模型 | async/await + Task | Goroutine + Channel |
| 类型系统 | 复杂、面向对象 | 简洁、结构化(结构体+接口) |
| 错误处理 | 异常 | 多返回值 + error |
| 泛型 | C# 2.0 起 | Go 1.18 起 |
| 包管理 | NuGet | Go Modules |
5.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; }
6. 常见陷阱与反模式
6.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"
}
}
6.2 顶级语句滥用
反模式:将整个企业级应用写在 Program.cs 顶级语句中,文件超过 2000 行。
对策:顶级语句仅用于启动器,业务逻辑分层到独立项目 / 类中。
6.3 var 滥用
反模式:
var result = DoSomething(); // result 是什么类型?
var x = GetX();
对策:
- 类型明显时使用
var:var list = new List<int>();。 - 类型不明显时显式声明:
Person person = GetPerson();。
6.4 异常吞咽
反模式:
try { DoSomething(); }
catch (Exception) { /* 啥也不做 */ }
对策:
- 至少记录日志:
catch (Exception e) { _logger.LogError(e, "Failed"); }。 - 区分可恢复与不可恢复异常。
6.5 同步阻塞异步方法
反模式:
var result = GetAsync().Result; // 死锁风险
var result = GetAsync().Wait();
对策:异步方法应 await,不要 .Result / .Wait()。详见《异步编程详解》。
6.6 装箱陷阱
反模式:
ArrayList list = new(); // 非泛型,所有 int 装箱
list.Add(1);
list.Add(2);
int sum = 0;
foreach (int i in list) sum += i;
对策:始终使用泛型集合 List<int>。
6.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();
6.8 配置文件未拷贝到输出目录
症状:发布后找不到 appsettings.json。
对策:在 .csproj 中:
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
6.9 NRT 与可空警告
症状:开启 NRT 后大量警告 CS8602: Dereference of a possibly null reference。
对策:
- 不要粗暴
!(null-forgiving)压制。 - 重新审视 null 来源,使用
??、?.、if (obj is not null)。 - 对外部输入显式校验:
ArgumentException.ThrowIfNull(arg)。
6.10 .NET Standard 滥用
反模式:所有新类库都建为 .NET Standard 2.0。
对策:
- 若库仅供 .NET 6/8 项目使用,目标框架设为
net8.0。 .NET Standard 2.0仅用于需要兼容 .NET Framework 4.6.1+ 与旧 Mono 的场景。
7. 工程实践与最佳实践
7.1 项目结构约定
推荐的解决方案目录结构:
flowchart TD
T0["MyApp/"]
T1["src/"]
T2["MyApp.Api/ # API 入口"]
T3["MyApp.Application/ # 应用服务层"]
T4["MyApp.Domain/ # 领域模型"]
T5["MyApp.Infrastructure/ # 基础设施"]
T6["MyApp.Tests/ # 测试"]
T7["tests/"]
T8["MyApp.UnitTests/"]
T9["MyApp.IntegrationTests/"]
T10["docs/"]
T11["scripts/"]
T12[".editorconfig"]
T13[".gitignore"]
T14["Directory.Build.props"]
T15["Directory.Packages.props"]
T16["global.json"]
T17["nuget.config"]
T18["MySolution.sln"]
T0 --> T1
T6 --> T7
T9 --> T10
T9 --> T11
T9 --> T12
T9 --> T13
T9 --> T14
T9 --> T15
T9 --> T16
T9 --> T17
T9 --> T18
7.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>
7.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>
7.4 global.json
{
"sdk": {
"version": "9.0.100",
"rollForward": "latestFeature",
"allowPrerelease": false
}
}
7.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
7.6 .gitignore
使用 dotnet new gitignore 生成默认模板,重点忽略:
bin/
obj/
*.user
*.suo
.vs/
.idea/
*.DS_Store
publish/
TestResults/
7.7 代码格式化
# 安装 dotnet-format
dotnet tool install -g dotnet-format
# 格式化整个解决方案
dotnet format MySolution.sln
# 仅检查,不修改
dotnet format --verify-no-changes
7.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
7.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);
}
}
7.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
7.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
7.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();
8. 案例研究
8.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 文档。
8.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 的子集。
8.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>
8.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>
8.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++;
}
8.6 案例:FANDEX 项目目录结构
FANDEX-Web 项目本身使用 Astro 框架,但本系列文档(C# 教程)涉及的所有示例项目结构如下:
flowchart TD
T0["FANDEX-csharp-samples/"]
T1["01-hello/"]
T2["02-async/"]
T3["03-linq/"]
T4["04-aspnetcore/"]
T5["05-efcore/"]
T6["06-maui/"]
T7["07-blazor/"]
T8["08-unity/"]
T9["09-source-generators/"]
T10["README.md"]
T0 --> T1
T0 --> T2
T0 --> T3
T0 --> T4
T0 --> T5
T0 --> T6
T0 --> T7
T0 --> T8
T0 --> T9
T0 --> T10
读者可在每篇文档末尾的”延伸阅读”找到对应的示例仓库链接。
8.7 案例:从 .NET Framework 迁移到 .NET 9
初始状态:
- .NET Framework 4.8 + ASP.NET MVC 5 + EF6。
- 部署在 Windows Server + IIS。
迁移步骤:
- 目标框架兼容性分析:使用
.NET Portability Analyzer工具。 - 替换不兼容 API:
AppDomain→AssemblyLoadContext;System.Web.HttpContext→Microsoft.AspNetCore.Http.HttpContext。 - EF6 → EF Core:使用
EF Core Power Tools逆向工程生成 DbContext。 - 迁移 ASP.NET MVC → ASP.NET Core MVC:Controller 与 View 大部分可平移。
- 配置文件:
web.config→appsettings.json+IConfiguration。 - DI:
Global.asax中的初始化 →Program.cs+IServiceCollection。 - 测试:xUnit + 测试容器化。
- 部署:IIS → Kestrel + Nginx 反向代理,或 Docker。
迁移收益:
- Linux 容器化部署,云成本降低 60%。
- 启动速度提升 3 倍(Kestrel 比 IIS pipeline 简化)。
- 内存占用降低 40%。
简答题知识点讲解
常见疑问 9:简述 C# 与 Java 在泛型实现上的差异。
解析讲解:
C# 在 CLR 层实现真泛型,每个值类型泛型实例化(如 List<int>)在运行时是独立的类型,底层为 int[],无装箱开销。引用类型泛型共享代码(如 List<string> 与 List<object> 共享同一份 JIT 代码),但类型安全由 CLR 保证。
Java 通过类型擦除(Type Erasure)实现泛型,编译后 List<Integer> 与 List<String> 都是 List,底层为 Object[]。这导致:
- 值类型存在装箱开销。
- 运行时无法获取泛型参数类型(
List<Integer>.class不存在)。 - 不能 new T()。
- 不能创建泛型数组。
C# 在这些场景下表现更好。
常见疑问 10:为什么 .NET Core 跨平台而 .NET Framework 不能?
解析讲解:
.NET Framework 设计于 2000 年代,深度耦合 Windows:
- BCL 依赖 Win32 API:
System.IO、System.Drawing、System.Diagnostics.Process等内部调用 Win32 函数。 - AppDomain:基于 Windows 进程模型。
- WCF / Web Forms / WPF:依赖 IIS、COM+、DirectX。
- GC:早期 Server GC 专为 Windows 设计。
- Registry:
Microsoft.Win32.Registry直接调用 Windows 注册表。
.NET Core 重写了这些子系统:
- PAL(Platform Adaptation Layer):抽象 OS 差异。
- BCL 重写:基于
System.IO.FileSystem、System.Net.Http等跨平台实现。 - Kestrel:自研跨平台 Web 服务器。
- Native P/Invoke:每个平台 P/Invoke 不同的系统库。
编程题知识点讲解
常见疑问 11:编写一个 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;
常见疑问 12:编写一个程序,将给定目录下所有 .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;
常见疑问 13:编写一个程序,使用 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");
9.6 综合应用题
常见疑问 16:你要为公司搭建一个内部微服务,技术选型在 .NET 8 与 Spring Boot 3 之间犹豫。请列出选型要点,并给出推荐。
参考答案要点:
| 维度 | .NET 8 | Spring 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.1 官方文档
- C# 语言参考:https://learn.microsoft.com/dotnet/csharp/language-reference/
- .NET 运行时仓库:https://github.com/dotnet/runtime
- Roslyn 编译器仓库:https://github.com/dotnet/roslyn
- C# 语言规范草案:https://github.com/dotnet/csharpstandard
- .NET Foundation:https://dotnetfoundation.org/
11.2 系列内交叉引用
继续学习 C# 的下一站:
- 基础语法 —— 类型、变量、运算符、控制流
- 面向对象编程 —— 类、接口、继承、多态
- 泛型与集合 ——
List<T>、Dictionary<K,V>、泛型约束 - 异步编程 —— Task、async/await、并行编程
- LINQ 与函数式编程 —— 查询表达式、Lambda
- 值类型与引用类型 —— 栈与堆、装箱拆箱
- GC 代机制 —— 分代回收、LOH、Server GC
- async-await 状态机 —— 编译器重写原理
- Span 与 Memory —— 零拷贝编程
11.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).
11.6 工具与扩展
- dotnet-format:代码格式化工具
- dotnet-ef:EF Core CLI 工具
- dotnet-counters:实时性能计数器
- dotnet-dump:堆转储分析
- dotnet-trace:性能追踪
- PerfView:免费性能分析工具
- dotMemory:JetBrains 内存分析(付费)
- dotTrace:JetBrains CPU 分析(付费)
- ILSpy / dnSpy:反编译工具
结语
至此,我们对 C# 语言与 .NET 平台建立了宏观认知。从 Anders Hejlsberg 在 2000 年的设计哲学,到 2024 年 .NET 9 的 Native AOT、HybridCache、动态 PGO,C# 已经从一个 Windows-only 语言演化为覆盖云、桌面、移动、游戏、IoT 的全栈平台。
下一步,请进入 基础语法,开始动手编写真正的 C# 代码。记住:
- 不要满足于”能跑”:理解每行代码背后发生了什么。
- 不要怕出错:错误是最好的老师,善用异常堆栈与调试器。
- 不要孤立学习:每一篇文档都关联着上下游,交叉阅读能加深理解。
愿你在 FANDEX 的 C# 之旅中,既能掌握工程实战,也能体味语言设计之美。