一、 3 个核心名词
1. 程序集(Assembly)
你写的 C# 代码编译后生成的文件,就是程序集。
- 常见格式:
.exe(可执行)、.dll(类库) - 它不是机器码,而是 IL 代码 + 元数据 的集合
- 一个程序集 = 一个 DLL / EXE
2. IL(Intermediate Language)中间语言
- C# 编译后不会直接变成 CPU 指令
- 而是变成一种统一的中间代码,叫 IL
- 它是平台无关的(Windows/Linux/macOS 都能用)
- IL 类似汇编,但和 CPU 无关
3. CLR(Common Language Runtime)
.NET 的运行时虚拟机,负责:
- 加载程序集
- 将 IL 编译成机器码(JIT)
- 内存管理(GC)
- 安全检查
- 执行代码
一句话:CLR 就是 .NET 程序的 "操作系统"
二、C# 程序完整生命周期
第一步:写 C# 代码 → 编译器编译
cs
Console.WriteLine("Hello");
C# 编译器(csc / Roslyn)做两件事:
- 把代码翻译成 IL 中间代码
- 生成 元数据(描述类、方法、字段、版本、依赖...)
最终输出:
程序集(.exe/.dll)
第二步:运行程序 → 启动 CLR
双击 exe 或 dotnet run 时:
- 操作系统启动 CLR 运行时
- CLR 加载程序集
- CLR 找到入口方法
Main()
第三步:JIT 编译器(即时编译)
重点:CLR 不会直接执行 IL IL 必须先变成 CPU 原生机器码
负责这件事的就是:
JIT(Just-In-Time)即时编译器
JIT 做什么?
- 第一次调用方法时
- 把 IL → 对应平台的原生机器码
- 缓存机器码,下次直接运行(超快)
这就是为什么:.NET 程序第一次运行慢一点,后面飞快
第四步:CPU 执行机器码
最终交给 CPU 执行的是:x86 /x64 / ARM 机器码
cs
C# 源代码
↓ (编译:csc/Roslyn)
IL 中间代码 + 元数据
↓ (打包)
程序集(.exe / .dll)
↓ (运行)
CLR 启动
↓ (JIT 编译)
CPU 原生机器码
↓
执行程序
三、IL 到底是什么?
cs
int a = 1;
int b = 2;
int c = a + b;
编译成 IL:
cs
ldc.i4.1
ldc.i4.2
add
stloc.2
特点:
- 栈操作
- 平台无关
- 所有 .NET 语言(C#/VB/F#)最终都变成 IL
- 这就是 .NET 多语言通用 的原因
四、程序集里面有什么?、
一个 .dll / .exe 程序集包含:
- IL 代码(方法实现)
- 元数据
- 类名、方法名、字段
- 依赖哪些程序集
- 版本、公钥、签名
- 清单(Manifest)
- 程序集版本
- 出口类型
- 安全信息
五、CLR 执行方法的完整流程
cs
1. 调用方法
2. CLR 检查方法是否已 JIT 编译
✅ 已编译 → 直接执行机器码
❌ 未编译 → JIT 把 IL → 机器码,缓存
3. 执行机器码
4. 运行结束
总结
- C# 编译 → 生成 IL,不是机器码
- IL 放在程序集里(exe/dll)
- CLR 是 .NET 虚拟机,负责加载和运行程序集
- JIT 将 IL 编译成平台原生机器码
- JIT 只编译一次,缓存后直接运行
源代码 → 编译成 IL → 放进程序集 → CLR 加载 → JIT 编译成机器码 → CPU 执行