1. 什么是 CIL?
CIL(Common Intermediate Language,公共中间语言)也常被称为 MSIL(Microsoft Intermediate Language),是.NET 框架 /.NET Core/.NET 5 + 体系中介于高级语言(C#/VB.NET等)和机器码之间的中间语言。
- 核心定位:C# 代码不会直接编译成 CPU 能执行的机器码,而是先编译成 CIL;程序运行时,.NET 的 JIT(即时编译器)会将 CIL 动态编译为当前平台(Windows/Linux/macOS)的原生机器码,这也是.NET "跨平台" 的核心基础之一。
- 关键特性 :
2. CIL 核心概念与基础指令
(1)CIL 的核心组成
- 指令 :最基础的执行单元,如
ldstr(加载字符串)、call(调用方法)、ret(返回); - 元数据:描述程序集、类型、方法、字段等信息(如类名、方法参数、访问修饰符);
- 资源:嵌入程序集的非代码文件(如图片、配置)。
(2)常用 CIL 指令
|------------|-----------------------------|
| 指令 | 作用 |
| ldarg.0 | 将第 0 个参数加载到评估栈(实例方法中this) |
| ldc.i4.x | 将整数 x 加载到栈(如ldc.i4.5加载 5) |
| ldstr | 将字符串常量加载到栈 |
| call | 调用静态方法 / 非虚实例方法 |
| callvirt | 调用虚方法 / 接口方法(动态分派) |
| stloc.0 | 将栈顶值存储到第 0 个局部变量 |
| ldloc.0 | 将第 0 个局部变量加载到栈 |
| add | 弹出栈顶两个值相加,结果入栈 |
| ret | 从方法返回(栈顶为返回值,无返回则空) |
3. CIL 实战示例(从 C# 到 CIL)
(1)第一步:编写简单 C# 代码
创建HelloCIL.cs,代码如下(核心功能:定义类 + 静态方法 + 实例方法,实现简单计算和输出):
cs
using System;
namespace CILDemo
{
public class Calculator
{
// 实例方法:两数相加
public int Add(int a, int b)
{
int sum = a + b;
Console.WriteLine($"相加结果:{sum}");
return sum;
}
// 静态方法:入口点
public static void Main()
{
Calculator calc = new Calculator();
int result = calc.Add(10, 20);
Console.WriteLine($"最终结果:{result}");
}
}
}
(2)第二步:编译 C# 为程序集(生成 CIL)
打开命令行(CMD/PowerShell),执行以下命令(需安装.NET SDK,确保csc或dotnet命令可用):
cs
# 方式1:使用csc(.NET Framework)
csc HelloCIL.cs -out:HelloCIL.exe
# 方式2:使用dotnet(.NET Core/.NET 5+)
dotnet new console -n CILDemo
# 替换Program.cs为上述代码后,执行
dotnet build
编译后生成HelloCIL.exe(或bin/Debug/net8.0/CILDemo.dll),此时文件内包含CIL 指令 + 元数据,还未生成机器码。
(3)第三步:查看 CIL 代码(反编译)
使用.NET 自带的ildasm(IL 反汇编器)或dnSpy/ildasm2工具查看 CIL:
方式 1:命令行执行ildasm HelloCIL.exe,打开图形化界面,展开Calculator类的Add和Main方法;
方式 2:使用dotnet ilasm(IL 汇编器)的反编译功能,或直接复制以下核心 CIL 代码(对应Add方法)。
Add方法的 CIL 代码(关键部分):
cs
.method public hidebysig instance int32
Add(int32 a,
int32 b) cil managed
{
// 方法体大小:28字节
.maxstack 3 // 评估栈最大深度
.locals init ([0] int32 sum, // 定义局部变量sum
[1] string V_1) // 临时字符串变量
// 1. 加载参数a到栈
IL_0000: ldarg.1
// 2. 加载参数b到栈
IL_0001: ldarg.2
// 3. 栈顶两个int相加,结果入栈
IL_0002: add
// 4. 栈顶结果存入局部变量sum(locals[0])
IL_0003: stloc.0
// 5. 加载字符串格式"相加结果:{sum}"到栈
IL_0004: ldstr "相加结果:{0}"
// 6. 加载sum到栈
IL_0009: ldloc.0
// 7. 调用string.Format方法(拼接字符串)
IL_000a: call string [System.Runtime]System.String::Format(string, object)
// 8. 存储拼接后的字符串到局部变量V_1
IL_000f: stloc.1
// 9. 加载V_1到栈
IL_0010: ldloc.1
// 10. 调用Console.WriteLine方法输出
IL_0011: call void [System.Console]System.Console::WriteLine(string)
// 11. 加载sum到栈(作为返回值)
IL_0016: ldloc.0
// 12. 方法返回,栈顶sum作为返回值
IL_0017: ret
} // end of method Calculator::Add
Main方法的核心 CIL(关键步骤):
cs
.method public hidebysig static void
Main() cil managed
{
.entrypoint // 标记为程序入口点
.maxstack 2
.locals init ([0] class CILDemo.Calculator calc,
[1] int32 result)
// 1. 新建Calculator实例(调用无参构造函数)
IL_0000: newobj instance void CILDemo.Calculator::.ctor()
// 2. 存储实例到局部变量calc
IL_0005: stloc.0
// 3. 加载calc实例到栈(实例方法调用需要)
IL_0006: ldloc.0
// 4. 加载第一个参数10到栈
IL_0007: ldc.i4.s 10
// 5. 加载第二个参数20到栈
IL_0009: ldc.i4.s 20
// 6. 调用Calculator的Add方法
IL_000b: callvirt instance int32 CILDemo.Calculator::Add(int32, int32)
// 7. 存储返回值到result
IL_0010: stloc.1
// 8. 加载字符串"最终结果:{0}"并调用Console.WriteLine
IL_0011: ldstr "最终结果:{0}"
IL_0016: ldloc.1
IL_0017: call string [System.Runtime]System.String::Format(string, object)
IL_001c: call void [System.Console]System.Console::WriteLine(string)
// 9. 无返回值,直接ret
IL_0021: ret
} // end of method Calculator::Main
(4)第四步:执行程序(JIT 编译 CIL 为机器码)
运行HelloCIL.exe(或dotnet run),输出如下:
cs
相加结果:30
最终结果:30
执行过程:
- CLR(公共语言运行时)加载程序集;
- JIT 编译器将
Main方法的 CIL 编译为当前 CPU 的机器码并执行; - 调用
Add方法时,JIT 再编译Add的 CIL 为机器码; - 执行完成后释放资源。
4. 与 CIL 相关的核心知识点
(1)CIL 与 CLR/JIT 的关系

- CLR:公共语言运行时,是.NET 的 "运行环境",负责加载程序集、管理内存、安全检查、执行 JIT 编译;
- JIT:即时编译器,核心作用是 "按需编译"------ 只编译当前执行的 CIL 方法,未执行的方法不编译,提升程序启动速度;
- AOT:提前编译(如.NET Native/.NET AOT),与 JIT 相反,直接将 CIL 编译为目标平台的机器码,无需运行时 JIT,适合跨平台发布或高性能场景。
(2)CIL 与程序集(Assembly)
- 程序集(.exe/.dll)是 CIL 的 "容器",包含:CIL 指令、元数据、资源;
- 元数据是 CIL 的 "说明书",描述程序集中的类型(类 / 结构体)、方法、字段、参数等,CLR 通过元数据识别 CIL 的执行逻辑;
- 强命名程序集:可给程序集签名,确保唯一性,避免版本冲突。
(3)CIL 的高级应用场景
- 反射 :.NET 反射的底层是读取程序集的元数据 + CIL,动态创建实例、调用方法(如
MethodInfo.Invoke); - AOP(面向切面编程):如 PostSharp、Castle DynamicProxy,通过修改 / 生成 CIL 实现 "无侵入" 的日志、事务、异常处理;
- 代码生成:如 EF Core 的表达式树编译、AutoMapper,动态生成 CIL 代码并编译为程序集,提升运行效率;
- 逆向工程 / 调试:分析第三方程序集的 CIL 代码,理解其逻辑。
(4)CIL 与 ILASM(IL 汇编器)
除了通过 C# 编译生成 CIL,还可以直接编写 CIL 代码 ,再用ilasm编译为程序集:
示例:编写Hello.il文件(纯 CIL 代码):
cs
.assembly Hello {}
.assembly extern mscorlib {}
.module Hello.exe
.class public HelloWorld
{
.method public static void Main() cil managed
{
.entrypoint
.maxstack 1
ldstr "Hello from raw CIL!"
call void [mscorlib]System.Console::WriteLine(string)
ret
}
}
编译命令:ilasm Hello.il,生成Hello.exe,运行后输出Hello from raw CIL!。
5.总结
- CIL 的本质:.NET 语言的 "中间态",跨语言 / 跨平台的核心,由高级语言编译生成,运行时被 JIT 编译为机器码;
- 核心特性:栈式执行、与平台 / 语言无关,依赖 CLR 运行;
- 核心关联 :
- CIL → JIT:运行时动态编译为机器码;
- CIL → 程序集:程序集是 CIL 的存储容器,包含元数据;
- CIL → 反射 / AOP:底层依赖读取 / 修改 CIL 实现动态逻辑;
- 学习价值:理解 CIL 能帮你深入掌握.NET 运行机制,排查性能问题、实现高级代码生成 / 动态编程。