c# 编译器 roslyn 是什么

来源:这里教程网 时间:2026-02-21 17:35:51 作者:

Roslyn 不是传统意义上“黑盒式”的编译器,它是微软开源的、可编程的 C#(和 VB.NET)编译器平台——你可以把它当成一个“带 API 的编译器”,而不是只能敲

dotnet build
的工具。

它把编译过程拆成可观察、可干预的阶段:语法分析 → 语义分析 → 绑定 → IL 生成。每个阶段都暴露了强类型的 API,让你能读取、遍历、修改甚至重新生成代码。


为什么 Roslyn 能解析代码还能改代码?

因为 Roslyn 把源码转成了两棵关键树:

SyntaxTree
(纯结构,不关心含义)和
Compilation
(带类型、符号、引用信息)。前者告诉你“代码长什么样”,后者告诉你“这段代码到底在干啥”。
比如你写
var x = new List<int>();</int>
SyntaxTree
只知道这是个变量声明,而
Compilation
能告诉你
List<int></int>
是来自
System.Collections.Generic
,且
var
推导为
List<int></int>

SyntaxTree
是不可变的,每次修改都返回新树(函数式风格,线程安全)
Compilation
需要显式添加引用(如
corlib
System.Runtime
),漏掉会报
CS0246: 未能找到类型或命名空间名
真实项目中,别手动拼
MetadataReference
,优先用
Microsoft.CodeAnalysis.CSharp.CSharpCompilation.Create()
+
.AddReferences(...)
或直接用
CSharpProject
(.NET SDK 内置方式)

动态编译字符串代码时最常卡在哪?

不是语法错,而是环境缺失。Roslyn 默认不带任何框架引用,哪怕只写

Console.WriteLine
,也得手动加
System.Console
对应的元数据引用。

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
<p>string code = @"using System; class A { static void Main() => Console.WriteLine(1); }";
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);</p><p>// ⚠️ 下面这步极易遗漏
CSharpCompilation compilation = CSharpCompilation.Create("tmp.dll")
.AddSyntaxTrees(tree)
.AddReferences(
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location)
)
.WithOptions(new CSharpCompilationOptions(OutputKind.ConsoleApplication));</p><p>// 编译失败?先看 diagnostics:
foreach (Diagnostic d in compilation.GetDiagnostics())
Console.WriteLine(d.ToString());</p>
如果用
CSharpScript
(脚本模式),它会自动加载常用引用,但仅限表达式/语句级执行,不能定义类或程序集
compilation.Emit()
返回
bool
,必须检查结果;失败时
GetDiagnostics()
才是真相

Roslyn 分析器(Analyzer)和源生成器(Source Generator)有什么区别?

两者都基于 Roslyn API,但生命周期和用途完全不同:

Analyzer
:只读分析,在 IDE 设计时或编译时报告诊断(警告/错误),不改变输出代码。例如检测未使用的变量、硬编码密码。

SourceGenerator
:在编译前介入,生成新的 .cs 文件并加入编译流程。例如从
[JsonSerializable]
特性自动生成序列化器代码。

分析器需打包为

.dll
+
.ruleset
或 NuGet,通过
<analyzer include="..."></analyzer>
引入

源生成器必须标记

[Generator] 
特性,且只能在
netstandard2.0
net6.0+
下运行;.NET SDK 6+ 默认启用,但需显式开启:
<emitcompilergeneratedfiles>true</emitcompilergeneratedfiles>


真正难的从来不是“怎么调 API”,而是理解 Roslyn 的两个世界:一个是语法层面的“字面结构”,一个是语义层面的“真实含义”。漏掉一次引用、误用

SyntaxFactory
而没设
Parent
、在生成器里试图访问尚未生成的符号——这些坑不会报编译错误,只会让生成结果为空或诊断静默失效。

相关推荐