Roslyn 原理
什么是 Roslyn?
Roslyn 是 .NET 编译器平台的代号,它是 C# 和 Visual Basic 的开源编译器。
编译流程
核心组件
1. 语法分析器 (Parser)
- 输入: 源代码文本
- 输出: 语法树 (SyntaxTree)
- 功能: 将文本转换为结构化的语法树
2. 语义分析器 (Semantic Analyzer)
- 输入: 语法树 + 引用
- 输出: 语义模型 (SemanticModel)
- 功能: 类型检查、符号解析、绑定
3. 源生成器 (Source Generator)
- 位置: 在语义分析之后,代码生成之前
- 功能: 分析代码并生成新的源文件
4. 代码生成器 (Code Generator)
- 输入: 语义模型
- 输出: IL 代码
- 功能: 生成中间语言代码
Roslyn API 架构
三层架构
编译器 API (源生成器使用这一层)
- 最底层的 API
- 提供完整的编译功能
- 包含语法树和语义模型
工作区 API
- 中间层 API
- 管理项目、解决方案和文档
- 用于 IDE 功能
脚本 API
- 最高层 API
- 提供动态代码执行
- 用于 REPL 场景
核心概念
语法树 (Syntax Tree)
- 源代码的结构化表示
- 包含所有信息(包括空格、注释)
- 不可变
语义模型 (Semantic Model)
- 提供类型信息
- 符号解析
- 类型检查
符号 (Symbol)
- 表示代码中的实体
- 类型、方法、属性、字段等
- 通过语义模型获取
下一步
Roslyn 的历史
编译器的演进
为什么需要 Roslyn?
传统编译器的问题:
黑盒设计
- 输入源代码,输出程序集
- 无法访问编译过程中的信息
- 难以构建代码分析工具
C++ 实现
- 难以扩展
- 无法在 .NET 中使用
- 维护成本高
缺乏 API
- IDE 功能需要重新实现
- 代码分析工具难以开发
- 无法复用编译器逻辑
Roslyn 的解决方案:
- ✅ 编译器即服务:提供完整的 API
- ✅ C# 实现:易于扩展和维护
- ✅ 开源:社区可以贡献
- ✅ 统一平台:IDE 和工具使用相同的编译器
编译管道详解
完整的编译流程
各阶段详解
1. 词法分析(Lexical Analysis)
功能:将源代码文本分解为 Token 序列
csharp
// 源代码
public class Person { }
// Token 序列
[
{ Kind: PublicKeyword, Text: "public" },
{ Kind: ClassKeyword, Text: "class" },
{ Kind: IdentifierToken, Text: "Person" },
{ Kind: OpenBraceToken, Text: "{" },
{ Kind: CloseBraceToken, Text: "}" }
]代码示例:
csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
// 词法分析示例
var code = "public class Person { }";
var syntaxTree = CSharpSyntaxTree.ParseText(code);
// 获取所有 Token
foreach (var token in syntaxTree.GetRoot().DescendantTokens())
{
Console.WriteLine($"Kind: {token.Kind()}, Text: '{token.Text}'");
}
// 输出:
// Kind: PublicKeyword, Text: 'public'
// Kind: ClassKeyword, Text: 'class'
// Kind: IdentifierToken, Text: 'Person'
// Kind: OpenBraceToken, Text: '{'
// Kind: CloseBraceToken, Text: '}'2. 语法分析(Syntax Analysis)
功能:将 Token 序列组织成语法树
csharp
// Token 序列 → 语法树
CompilationUnit
└── ClassDeclaration (Person)
├── Modifiers: public
├── Keyword: class
├── Identifier: Person
└── Members: (empty)代码示例:
csharp
using Microsoft.CodeAnalysis.CSharp.Syntax;
var code = @"
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}";
var tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetRoot();
// 查找类声明
var classDecl = root.DescendantNodes()
.OfType<ClassDeclarationSyntax>()
.First();
Console.WriteLine($"Class Name: {classDecl.Identifier.Text}");
Console.WriteLine($"Modifiers: {string.Join(" ", classDecl.Modifiers)}");
Console.WriteLine($"Properties: {classDecl.Members.Count}");
// 输出:
// Class Name: Person
// Modifiers: public
// Properties: 23. 语义分析(Semantic Analysis)
功能:类型检查、符号解析、绑定
csharp
// 语法树 + 引用 → 语义模型
Person person = new Person();
↓ ↓
类型检查 构造函数解析代码示例:
csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
var code = @"
class Person
{
public string Name { get; set; }
}
class Program
{
void Main()
{
var person = new Person();
person.Name = ""张三"";
}
}";
// 创建编译
var tree = CSharpSyntaxTree.ParseText(code);
var compilation = CSharpCompilation.Create("MyCompilation")
.AddReferences(MetadataReference.CreateFromFile(
typeof(object).Assembly.Location))
.AddSyntaxTrees(tree);
// 获取语义模型
var semanticModel = compilation.GetSemanticModel(tree);
// 查找变量声明
var root = tree.GetRoot();
var variableDecl = root.DescendantNodes()
.OfType<VariableDeclaratorSyntax>()
.First(v => v.Identifier.Text == "person");
// 获取类型信息
var typeInfo = semanticModel.GetTypeInfo(variableDecl.Parent.Parent);
Console.WriteLine($"Variable Type: {typeInfo.Type.Name}");
// 输出: Variable Type: Person4. 源生成(Source Generation)
功能:分析代码并生成新的源文件
csharp
// 语义模型 → 源生成器 → 新源文件
[GenerateToString]
public partial class Person { }
↓
源生成器分析
↓
public partial class Person
{
public override string ToString() => /* ... */;
}代码示例:
csharp
[Generator]
public class ToStringGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// 1. 获取编译信息
var compilation = context.Compilation;
// 2. 查找标记的类
foreach (var syntaxTree in compilation.SyntaxTrees)
{
var semanticModel = compilation.GetSemanticModel(syntaxTree);
var root = syntaxTree.GetRoot();
foreach (var classDecl in root.DescendantNodes()
.OfType<ClassDeclarationSyntax>())
{
var symbol = semanticModel.GetDeclaredSymbol(classDecl);
// 3. 检查是否有特性
if (HasGenerateToStringAttribute(symbol))
{
// 4. 生成代码
var code = GenerateToStringMethod(symbol);
context.AddSource($"{symbol.Name}.g.cs", code);
}
}
}
}
}5. IL 生成(IL Emission)
功能:将语义模型转换为 IL 代码
csharp
// 语义模型 → IL 代码
public string Name { get; set; }
↓
.property instance string Name()
{
.get instance string Person::get_Name()
.set instance void Person::set_Name(string)
}语法树、语义模型和符号的关系
三者的关系图
详细说明
语法树(SyntaxTree)
- 源代码的结构化表示
- 包含所有文本信息(包括空格、注释)
- 不包含类型信息
- 不可变
语义模型(SemanticModel)
- 连接语法树和符号的桥梁
- 提供类型信息
- 提供符号解析
- 每个语法树对应一个语义模型
符号(Symbol)
- 表示代码中的实体
- 类型、方法、属性、字段等
- 包含完整的元数据
- 可以跨文件引用
代码示例:三者协同工作
csharp
var code = @"
class Person
{
public string Name { get; set; }
public void SayHello()
{
Console.WriteLine($""Hello, {Name}"");
}
}";
// 1. 创建语法树
var tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetRoot();
// 2. 创建编译和语义模型
var compilation = CSharpCompilation.Create("Test")
.AddReferences(MetadataReference.CreateFromFile(
typeof(object).Assembly.Location))
.AddSyntaxTrees(tree);
var semanticModel = compilation.GetSemanticModel(tree);
// 3. 查找方法声明(语法树)
var methodDecl = root.DescendantNodes()
.OfType<MethodDeclarationSyntax>()
.First(m => m.Identifier.Text == "SayHello");
Console.WriteLine($"Method Name (Syntax): {methodDecl.Identifier.Text}");
// 4. 获取方法符号(语义模型 + 符号)
var methodSymbol = semanticModel.GetDeclaredSymbol(methodDecl) as IMethodSymbol;
Console.WriteLine($"Method Name (Symbol): {methodSymbol.Name}");
Console.WriteLine($"Return Type: {methodSymbol.ReturnType.Name}");
Console.WriteLine($"Containing Type: {methodSymbol.ContainingType.Name}");
// 5. 查找方法体中的标识符
var identifier = methodDecl.DescendantNodes()
.OfType<IdentifierNameSyntax>()
.First(i => i.Identifier.Text == "Name");
// 6. 获取标识符的符号信息
var symbolInfo = semanticModel.GetSymbolInfo(identifier);
var propertySymbol = symbolInfo.Symbol as IPropertySymbol;
Console.WriteLine($"Property Name: {propertySymbol.Name}");
Console.WriteLine($"Property Type: {propertySymbol.Type.Name}");
// 输出:
// Method Name (Syntax): SayHello
// Method Name (Symbol): SayHello
// Return Type: Void
// Containing Type: Person
// Property Name: Name
// Property Type: String使用场景对比
| 需求 | 使用工具 | 示例 |
|---|---|---|
| 查找类名 | 语法树 | classDecl.Identifier.Text |
| 获取类型信息 | 语义模型 | semanticModel.GetTypeInfo(node) |
| 检查继承关系 | 符号 | symbol.BaseType |
| 查找所有引用 | 符号 | SymbolFinder.FindReferencesAsync |
| 格式化代码 | 语法树 | node.NormalizeWhitespace() |
| 类型转换检查 | 语义模型 | semanticModel.ClassifyConversion |
实际应用示例
示例 1: 查找所有公共类
csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
public class PublicClassFinder
{
public static List<string> FindPublicClasses(string code)
{
var tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetRoot();
var publicClasses = root.DescendantNodes()
.OfType<ClassDeclarationSyntax>()
.Where(c => c.Modifiers.Any(SyntaxKind.PublicKeyword))
.Select(c => c.Identifier.Text)
.ToList();
return publicClasses;
}
}
// 使用
var code = @"
public class Person { }
internal class Employee { }
public class Customer { }
";
var classes = PublicClassFinder.FindPublicClasses(code);
Console.WriteLine(string.Join(", ", classes));
// 输出: Person, Customer示例 2: 查找实现特定接口的类
csharp
public class InterfaceImplementationFinder
{
public static List<string> FindImplementations(
Compilation compilation,
string interfaceName)
{
var results = new List<string>();
foreach (var syntaxTree in compilation.SyntaxTrees)
{
var semanticModel = compilation.GetSemanticModel(syntaxTree);
var root = syntaxTree.GetRoot();
foreach (var classDecl in root.DescendantNodes()
.OfType<ClassDeclarationSyntax>())
{
var classSymbol = semanticModel.GetDeclaredSymbol(classDecl);
// 检查是否实现了指定接口
if (classSymbol.AllInterfaces.Any(i => i.Name == interfaceName))
{
results.Add(classSymbol.Name);
}
}
}
return results;
}
}
// 使用
var code = @"
interface IRepository { }
class UserRepository : IRepository { }
class ProductRepository : IRepository { }
class Logger { }
";
var tree = CSharpSyntaxTree.ParseText(code);
var compilation = CSharpCompilation.Create("Test")
.AddSyntaxTrees(tree);
var implementations = InterfaceImplementationFinder
.FindImplementations(compilation, "IRepository");
Console.WriteLine(string.Join(", ", implementations));
// 输出: UserRepository, ProductRepository示例 3: 分析方法复杂度
csharp
public class ComplexityAnalyzer
{
public static int CalculateCyclomaticComplexity(MethodDeclarationSyntax method)
{
int complexity = 1; // 基础复杂度
// 统计决策点
var decisionPoints = method.DescendantNodes()
.Where(node =>
node is IfStatementSyntax ||
node is WhileStatementSyntax ||
node is ForStatementSyntax ||
node is ForEachStatementSyntax ||
node is CaseSwitchLabelSyntax ||
node is ConditionalExpressionSyntax ||
node is BinaryExpressionSyntax binary &&
(binary.IsKind(SyntaxKind.LogicalAndExpression) ||
binary.IsKind(SyntaxKind.LogicalOrExpression)))
.Count();
complexity += decisionPoints;
return complexity;
}
}
// 使用
var code = @"
public int CalculateDiscount(int age, bool isMember, decimal amount)
{
if (age < 18)
return 0;
if (isMember && amount > 100)
return 20;
else if (amount > 50)
return 10;
return 5;
}";
var tree = CSharpSyntaxTree.ParseText(code);
var method = tree.GetRoot()
.DescendantNodes()
.OfType<MethodDeclarationSyntax>()
.First();
var complexity = ComplexityAnalyzer.CalculateCyclomaticComplexity(method);
Console.WriteLine($"Cyclomatic Complexity: {complexity}");
// 输出: Cyclomatic Complexity: 5性能考虑
Roslyn API 的性能特点
性能最佳实践
1. 重用编译对象
csharp
// ✅ 推荐:重用编译对象
var compilation = CSharpCompilation.Create("MyCompilation")
.AddReferences(references)
.AddSyntaxTrees(trees);
// 多次使用同一个编译对象
var semanticModel1 = compilation.GetSemanticModel(tree1);
var semanticModel2 = compilation.GetSemanticModel(tree2);
// ❌ 避免:每次都创建新的编译对象
foreach (var tree in trees)
{
var compilation = CSharpCompilation.Create("MyCompilation")
.AddReferences(references)
.AddSyntaxTrees(tree); // 重复创建,性能差
}2. 使用 SyntaxWalker 而不是递归
csharp
// ✅ 推荐:使用 SyntaxWalker
public class ClassCollector : CSharpSyntaxWalker
{
public List<ClassDeclarationSyntax> Classes { get; } = new();
public override void VisitClassDeclaration(ClassDeclarationSyntax node)
{
Classes.Add(node);
base.VisitClassDeclaration(node);
}
}
var walker = new ClassCollector();
walker.Visit(root);
var classes = walker.Classes;
// ❌ 避免:手动递归遍历
void CollectClasses(SyntaxNode node, List<ClassDeclarationSyntax> classes)
{
if (node is ClassDeclarationSyntax classDecl)
classes.Add(classDecl);
foreach (var child in node.ChildNodes())
CollectClasses(child, classes); // 递归,性能较差
}3. 避免不必要的语义分析
csharp
// ✅ 推荐:先用语法过滤,再用语义分析
var candidateClasses = root.DescendantNodes()
.OfType<ClassDeclarationSyntax>()
.Where(c => c.AttributeLists.Count > 0); // 语法过滤
foreach (var classDecl in candidateClasses)
{
var symbol = semanticModel.GetDeclaredSymbol(classDecl); // 只对候选类进行语义分析
if (HasSpecificAttribute(symbol))
{
// 处理...
}
}
// ❌ 避免:对所有类进行语义分析
foreach (var classDecl in root.DescendantNodes().OfType<ClassDeclarationSyntax>())
{
var symbol = semanticModel.GetDeclaredSymbol(classDecl); // 对所有类都进行语义分析
if (HasSpecificAttribute(symbol))
{
// 处理...
}
}4. 使用 LINQ 的延迟执行
csharp
// ✅ 推荐:使用延迟执行
var publicMethods = root.DescendantNodes()
.OfType<MethodDeclarationSyntax>()
.Where(m => m.Modifiers.Any(SyntaxKind.PublicKeyword))
.Take(10); // 只取前 10 个
// ❌ 避免:立即执行
var allMethods = root.DescendantNodes()
.OfType<MethodDeclarationSyntax>()
.ToList(); // 立即执行,处理所有方法
var publicMethods = allMethods
.Where(m => m.Modifiers.Any(SyntaxKind.PublicKeyword))
.Take(10);性能测试示例
csharp
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
[MemoryDiagnoser]
public class RoslynPerformanceBenchmark
{
private string _code;
private SyntaxTree _tree;
private Compilation _compilation;
[GlobalSetup]
public void Setup()
{
_code = GenerateLargeCodeFile(); // 生成大型代码文件
_tree = CSharpSyntaxTree.ParseText(_code);
_compilation = CSharpCompilation.Create("Test")
.AddSyntaxTrees(_tree);
}
[Benchmark]
public int CountClasses_WithSyntaxWalker()
{
var walker = new ClassCountWalker();
walker.Visit(_tree.GetRoot());
return walker.Count;
}
[Benchmark]
public int CountClasses_WithLinq()
{
return _tree.GetRoot()
.DescendantNodes()
.OfType<ClassDeclarationSyntax>()
.Count();
}
}
// 结果示例:
// | Method | Mean | Allocated |
// |------------------------------ |----------:|----------:|
// | CountClasses_WithSyntaxWalker | 45.2 ms | 1.2 KB |
// | CountClasses_WithLinq | 52.8 ms | 15.3 KB |最佳实践
1. 使用正确的 API 层次
csharp
// ✅ 源生成器:使用编译器 API
[Generator]
public class MyGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
var compilation = context.Compilation; // 编译器 API
// ...
}
}
// ✅ IDE 扩展:使用工作区 API
public class MyCodeRefactoring : CodeRefactoringProvider
{
public override async Task ComputeRefactoringsAsync(
CodeRefactoringContext context)
{
var document = context.Document; // 工作区 API
// ...
}
}
// ✅ 脚本执行:使用脚本 API
var script = CSharpScript.Create("Console.WriteLine(\"Hello\")");
await script.RunAsync(); // 脚本 API2. 正确处理语法树的不可变性
csharp
// ✅ 推荐:使用 With 方法创建修改后的节点
var originalClass = SyntaxFactory.ClassDeclaration("Person");
var modifiedClass = originalClass
.WithModifiers(SyntaxFactory.TokenList(
SyntaxFactory.Token(SyntaxKind.PublicKeyword)))
.WithMembers(SyntaxFactory.List(members));
// ❌ 避免:尝试修改现有节点(不可能,因为不可变)
// originalClass.Modifiers = ...; // 编译错误3. 使用 SyntaxFactory 创建语法节点
csharp
// ✅ 推荐:使用 SyntaxFactory
var propertyDecl = SyntaxFactory.PropertyDeclaration(
SyntaxFactory.PredefinedType(
SyntaxFactory.Token(SyntaxKind.StringKeyword)),
"Name")
.WithModifiers(SyntaxFactory.TokenList(
SyntaxFactory.Token(SyntaxKind.PublicKeyword)))
.WithAccessorList(SyntaxFactory.AccessorList(
SyntaxFactory.List(new[]
{
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
})));
// 生成: public string Name { get; set; }
// ❌ 避免:手动拼接字符串
var code = "public string Name { get; set; }";
var tree = CSharpSyntaxTree.ParseText(code); // 不够灵活4. 使用 Roslyn Quoter 工具
Roslyn Quoter 是一个在线工具,可以将 C# 代码转换为 SyntaxFactory 调用:
csharp
// 输入代码:
public string Name { get; set; }
// Roslyn Quoter 输出:
SyntaxFactory.PropertyDeclaration(
SyntaxFactory.PredefinedType(
SyntaxFactory.Token(SyntaxKind.StringKeyword)),
SyntaxFactory.Identifier("Name"))
.WithModifiers(
SyntaxFactory.TokenList(
SyntaxFactory.Token(SyntaxKind.PublicKeyword)))
.WithAccessorList(
SyntaxFactory.AccessorList(
SyntaxFactory.List<AccessorDeclarationSyntax>(
new AccessorDeclarationSyntax[]{
SyntaxFactory.AccessorDeclaration(
SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(
SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
SyntaxFactory.AccessorDeclaration(
SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(
SyntaxFactory.Token(SyntaxKind.SemicolonToken))})));工具地址:https://roslynquoter.azurewebsites.net/
5. 处理 Trivia(空格、注释等)
csharp
// ✅ 推荐:保留或添加适当的 Trivia
var classDecl = SyntaxFactory.ClassDeclaration("Person")
.WithLeadingTrivia(
SyntaxFactory.Comment("// This is a person class"),
SyntaxFactory.CarriageReturnLineFeed)
.WithModifiers(SyntaxFactory.TokenList(
SyntaxFactory.Token(
SyntaxFactory.TriviaList(), // Leading trivia
SyntaxKind.PublicKeyword,
SyntaxFactory.TriviaList(SyntaxFactory.Space)))); // Trailing trivia
// 生成:
// // This is a person class
// public class Person
// ❌ 避免:忽略 Trivia,导致格式混乱
var classDecl = SyntaxFactory.ClassDeclaration("Person")
.WithModifiers(SyntaxFactory.TokenList(
SyntaxFactory.Token(SyntaxKind.PublicKeyword)));
// 生成: publicclass Person // 缺少空格常见问题
Q1: 语法树和 AST 有什么区别?
A: Roslyn 的语法树比传统的 AST(抽象语法树)更完整:
| 特性 | 传统 AST | Roslyn 语法树 |
|---|---|---|
| 空格 | 丢失 | 保留 |
| 注释 | 丢失 | 保留 |
| 格式 | 丢失 | 保留 |
| 错误 | 停止解析 | 继续解析 |
| 用途 | 编译 | 编译 + 工具 |
Roslyn 的语法树是"无损"的,可以完全重建原始源代码。
Q2: 如何处理语法错误?
A: Roslyn 会继续解析,并在语法树中标记错误:
csharp
var code = @"
public class Person
{
public string Name { get; set; }
public int Age { get set; } // 语法错误:缺少 ;
}";
var tree = CSharpSyntaxTree.ParseText(code);
// 检查诊断信息
var diagnostics = tree.GetDiagnostics();
foreach (var diagnostic in diagnostics)
{
Console.WriteLine($"{diagnostic.Severity}: {diagnostic.GetMessage()}");
Console.WriteLine($"Location: {diagnostic.Location.GetLineSpan()}");
}
// 输出:
// Error: ; expected
// Location: (5,23)-(5,23)
// 语法树仍然可用
var root = tree.GetRoot();
var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>();
Console.WriteLine($"Found {classes.Count()} classes");
// 输出: Found 1 classesQ3: 语义模型的生命周期是什么?
A: 语义模型与编译对象绑定:
csharp
// 创建编译
var compilation = CSharpCompilation.Create("Test")
.AddSyntaxTrees(tree);
// 获取语义模型
var semanticModel = compilation.GetSemanticModel(tree);
// 语义模型缓存在编译对象中
var semanticModel2 = compilation.GetSemanticModel(tree);
// semanticModel == semanticModel2 (同一个对象)
// 修改编译会使语义模型失效
var newCompilation = compilation.AddSyntaxTrees(anotherTree);
var newSemanticModel = newCompilation.GetSemanticModel(tree);
// newSemanticModel != semanticModel (不同的对象)Q4: 如何在多个文件之间共享符号?
A: 符号在编译对象中共享:
csharp
var file1 = @"
namespace MyNamespace
{
public class Person { }
}";
var file2 = @"
namespace MyNamespace
{
public class Employee : Person { }
}";
var tree1 = CSharpSyntaxTree.ParseText(file1);
var tree2 = CSharpSyntaxTree.ParseText(file2);
// 创建包含两个文件的编译
var compilation = CSharpCompilation.Create("Test")
.AddSyntaxTrees(tree1, tree2);
// 在 file2 中可以访问 file1 中定义的 Person
var semanticModel2 = compilation.GetSemanticModel(tree2);
var employeeClass = tree2.GetRoot()
.DescendantNodes()
.OfType<ClassDeclarationSyntax>()
.First();
var employeeSymbol = semanticModel2.GetDeclaredSymbol(employeeClass);
var baseType = employeeSymbol.BaseType;
Console.WriteLine($"Base Type: {baseType.Name}");
// 输出: Base Type: PersonQ5: Roslyn 支持哪些语言?
A: Roslyn 主要支持:
C#
- 完整支持
- 最新语言特性
- 源生成器支持
Visual Basic
- 完整支持
- 维护模式
- 有限的新特性
F#
- 使用独立的编译器
- 不是 Roslyn 的一部分
下一步
现在你已经了解了 Roslyn 的基础知识!接下来可以:
深入学习核心概念
查看实际示例
- Hello World 示例 - 最简单的源生成器
- ToString 生成器 - 实用的代码生成
- 增量生成器 - 性能优化技术
- 诊断报告 - 错误和警告处理
API 参考
工具和资源
- Roslyn GitHub - 源代码和示例
- Roslyn Quoter - 代码转换工具
- Roslyn API 浏览器 - API 文档
- Roslyn SDK - SDK 和模板
🔗 相关资源
深入学习
- 什么是源生成器 - 了解源生成器的概念和原理
- 编译流程 - 了解编译流程和源生成器的执行时机
- 语法树 - 深入理解语法树的结构和操作
- 语义模型 - 掌握语义模型的使用方法
- 符号系统 - 了解符号系统的详细信息
API 参考
实战示例
- Hello World 示例 - 最简单的入门示例
- 增量生成器示例 - 学习增量生成器的使用
最后更新: 2025-01-21
Roslyn 性能特性
编译器性能优化
Roslyn 编译器采用了多种优化技术来提高编译速度:
1. 增量编译
csharp
// Roslyn 只重新编译变化的文件
// 未变化的文件使用缓存的结果
// 示例:修改一个文件
// File1.cs - 未修改 → 使用缓存
// File2.cs - 已修改 → 重新编译
// File3.cs - 未修改 → 使用缓存优势:
- ✅ 大幅减少编译时间
- ✅ 提高开发效率
- ✅ 降低 CPU 和内存使用
2. 并行编译
Roslyn 可以并行编译多个文件:
csharp
// 配置并行编译
<PropertyGroup>
<UseSharedCompilation>true</UseSharedCompilation>
<BuildInParallel>true</BuildInParallel>
</PropertyGroup>3. 共享编译服务器
csharp
// VBCSCompiler.exe 作为后台服务
// 多个编译任务共享同一个编译器实例
// 减少启动开销和内存占用
// 查看编译服务器状态
dotnet build-server shutdown内存管理
Roslyn 使用高效的内存管理策略:
不可变数据结构
csharp
// 语法树节点是不可变的
var oldNode = syntaxTree.GetRoot();
var newNode = oldNode.ReplaceNode(targetNode, newTargetNode);
// oldNode 仍然有效,可以安全共享
// newNode 是新的树,共享未修改的部分优势:
- ✅ 线程安全
- ✅ 结构共享,节省内存
- ✅ 支持撤销/重做
- ✅ 简化并发编程
对象池
csharp
// Roslyn 使用对象池减少 GC 压力
// 频繁创建的对象被重用
// 示例:StringBuilder 池
var builder = StringBuilderPool.Allocate();
try
{
builder.Append("code");
return builder.ToString();
}
finally
{
StringBuilderPool.Free(builder);
}API 性能考虑
使用 Roslyn API 时的性能建议:
✅ 推荐做法
csharp
// 1. 缓存 SemanticModel
var semanticModel = compilation.GetSemanticModel(syntaxTree);
foreach (var node in nodes)
{
var symbol = semanticModel.GetSymbolInfo(node).Symbol;
// 重用 semanticModel
}
// 2. 使用 SyntaxWalker 而不是递归
public class MyWalker : SyntaxWalker
{
public override void VisitClassDeclaration(ClassDeclarationSyntax node)
{
// 高效遍历
base.VisitClassDeclaration(node);
}
}
// 3. 使用 SymbolEqualityComparer
var comparer = SymbolEqualityComparer.Default;
if (comparer.Equals(symbol1, symbol2))
{
// 正确的符号比较
}❌ 避免的做法
csharp
// 1. 重复创建 SemanticModel
foreach (var node in nodes)
{
// ❌ 每次都创建新的 SemanticModel
var semanticModel = compilation.GetSemanticModel(syntaxTree);
var symbol = semanticModel.GetSymbolInfo(node).Symbol;
}
// 2. 使用递归遍历大型语法树
void TraverseNode(SyntaxNode node)
{
// ❌ 可能导致栈溢出
foreach (var child in node.ChildNodes())
{
TraverseNode(child);
}
}
// 3. 使用 == 比较符号
if (symbol1 == symbol2) // ❌ 错误的比较方式
{
// 可能得到错误结果
}编译时间优化
实际项目中的优化技巧:
xml
<!-- 1. 启用确定性构建 -->
<PropertyGroup>
<Deterministic>true</Deterministic>
</PropertyGroup>
<!-- 2. 禁用不需要的分析器 -->
<PropertyGroup>
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>
</PropertyGroup>
<!-- 3. 使用增量源生成器 -->
<ItemGroup>
<PackageReference Include="MyGenerator" Version="1.0.0"
OutputItemType="Analyzer" />
</ItemGroup>
<!-- 4. 配置并行度 -->
<PropertyGroup>
<MaxCpuCount>0</MaxCpuCount> <!-- 使用所有 CPU -->
</PropertyGroup>性能监控
监控编译性能的工具:
bash
# 1. 详细构建日志
dotnet build -v detailed
# 2. 二进制日志
dotnet build -bl:build.binlog
# 3. 使用 MSBuild Structured Log Viewer
# 下载:https://msbuildlog.com/
# 4. 性能分析
dotnet build -p:ReportAnalyzer=trueRoslyn 扩展性
分析器 (Analyzers)
分析器可以检测代码问题:
csharp
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MyAnalyzer : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Rule);
private static readonly DiagnosticDescriptor Rule = new(
"MY001",
"Title",
"Message",
"Category",
DiagnosticSeverity.Warning,
isEnabledByDefault: true);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeNode,
SyntaxKind.ClassDeclaration);
}
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
var classDecl = (ClassDeclarationSyntax)context.Node;
// 分析逻辑
if (HasIssue(classDecl))
{
context.ReportDiagnostic(Diagnostic.Create(
Rule, classDecl.GetLocation()));
}
}
}代码修复 (Code Fixes)
代码修复可以自动修复问题:
csharp
[ExportCodeFixProvider(LanguageNames.CSharp)]
public class MyCodeFixProvider : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create("MY001");
public override async Task RegisterCodeFixesAsync(
CodeFixContext context)
{
var root = await context.Document
.GetSyntaxRootAsync(context.CancellationToken);
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
var node = root.FindNode(diagnosticSpan);
context.RegisterCodeFix(
CodeAction.Create(
"Fix issue",
c => FixIssueAsync(context.Document, node, c),
equivalenceKey: "FixIssue"),
diagnostic);
}
private async Task<Document> FixIssueAsync(
Document document,
SyntaxNode node,
CancellationToken cancellationToken)
{
// 修复逻辑
var newNode = /* 创建修复后的节点 */;
var root = await document.GetSyntaxRootAsync(cancellationToken);
var newRoot = root.ReplaceNode(node, newNode);
return document.WithSyntaxRoot(newRoot);
}
}重构 (Refactorings)
重构提供代码转换功能:
csharp
[ExportCodeRefactoringProvider(LanguageNames.CSharp)]
public class MyRefactoringProvider : CodeRefactoringProvider
{
public override async Task ComputeRefactoringsAsync(
CodeRefactoringContext context)
{
var root = await context.Document
.GetSyntaxRootAsync(context.CancellationToken);
var node = root.FindNode(context.Span);
if (node is ClassDeclarationSyntax classDecl)
{
context.RegisterRefactoring(
CodeAction.Create(
"Convert to record",
c => ConvertToRecordAsync(
context.Document, classDecl, c),
equivalenceKey: "ConvertToRecord"));
}
}
private async Task<Document> ConvertToRecordAsync(
Document document,
ClassDeclarationSyntax classDecl,
CancellationToken cancellationToken)
{
// 转换逻辑
var recordDecl = /* 创建 record 声明 */;
var root = await document.GetSyntaxRootAsync(cancellationToken);
var newRoot = root.ReplaceNode(classDecl, recordDecl);
return document.WithSyntaxRoot(newRoot);
}
}