Roslyn API 介绍
📋 文档信息
难度: 🟢 简单
预计阅读时间: 30 分钟
前置知识:
- C# 基础语法
- 理解源生成器原理
适合人群:
- 准备开发源生成器的开发者
- 需要了解 Roslyn API 的开发者
📋 快速导航
| 章节 | 难度 | 阅读时间 | 链接 |
|---|---|---|---|
| 核心命名空间 | 🟢 | 5 分钟 | 查看 |
| 生成器接口 | 🟢 | 15 分钟 | 查看 |
| API 概览 | 🟡 | 10 分钟 | 查看 |
🎯 概览
本文档介绍 Roslyn API 的核心概念和生成器接口,包括命名空间、ISourceGenerator、IIncrementalGenerator 和 ForAttributeWithMetadataName 等关键 API。
本文档涵盖:
- Roslyn 核心命名空间和包引用
- 传统生成器接口 (ISourceGenerator)
- 增量生成器接口 (IIncrementalGenerator) - 推荐使用
- .NET 7+ 高性能 API (ForAttributeWithMetadataName)
典型应用场景:
- 选择合适的生成器接口
- 理解生成器的生命周期
- 掌握基本的 API 使用方法
核心命名空间
必需的命名空间
csharp
using Microsoft.CodeAnalysis; // 核心抽象(ISymbol, Compilation, SyntaxTree 等)
using Microsoft.CodeAnalysis.CSharp; // C# 特定实现(CSharpCompilation, CSharpSyntaxTree 等)
using Microsoft.CodeAnalysis.CSharp.Syntax; // C# 语法节点(ClassDeclarationSyntax, MethodDeclarationSyntax 等)
using Microsoft.CodeAnalysis.Text; // 文本处理(SourceText, TextSpan 等)
using System.Collections.Immutable; // 不可变集合(ImmutableArray 等)命名空间说明
- Microsoft.CodeAnalysis: 包含所有语言通用的核心抽象,如符号(ISymbol)、编译(Compilation)、诊断(Diagnostic)等
- Microsoft.CodeAnalysis.CSharp: C# 语言特定的实现,包括 C# 编译器和语法树
- Microsoft.CodeAnalysis.CSharp.Syntax: C# 语法节点的具体类型,每种语法结构都有对应的类
- Microsoft.CodeAnalysis.Text: 文本处理相关的类型,用于处理源代码文本
- System.Collections.Immutable: Roslyn 大量使用不可变集合以确保线程安全和性能
NuGet 包引用
xml
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
</ItemGroup>生成器接口
ISourceGenerator (传统生成器)
概述
传统的源生成器接口,在 .NET 5 中引入。虽然功能完整,但性能不如增量生成器。
适用场景:
- 简单的代码生成任务
- 不需要频繁重新生成
- 学习和原型开发
完整示例
csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
using System.Linq;
using System.Text;
[Generator]
public class MyGenerator : ISourceGenerator
{
/// <summary>
/// 初始化生成器
/// 在编译开始时调用一次,用于注册回调和设置生成器
/// </summary>
public void Initialize(GeneratorInitializationContext context)
{
// 1. 注册语法接收器(用于收集感兴趣的语法节点)
context.RegisterForSyntaxNotifications(() => new MySyntaxReceiver());
// 2. 注册后初始化回调(用于生成在整个编译过程中都需要的代码)
context.RegisterForPostInitialization(ctx => {
// 生成特性定义
ctx.AddSource("MyAttribute.g.cs", SourceText.From(@"
using System;
namespace MyNamespace
{
[AttributeUsage(AttributeTargets.Class)]
internal class MyAttribute : Attribute { }
}", Encoding.UTF8));
});
}
/// <summary>
/// 执行代码生成
/// 在编译过程中被调用,可以访问完整的编译信息
/// </summary>
public void Execute(GeneratorExecutionContext context)
{
// 1. 访问编译信息
Compilation compilation = context.Compilation;
// 2. 获取语法接收器收集的节点
if (context.SyntaxReceiver is not MySyntaxReceiver receiver)
return;
// 3. 遍历收集的类
foreach (var classDecl in receiver.CandidateClasses)
{
// 获取语义模型
var model = compilation.GetSemanticModel(classDecl.SyntaxTree);
var classSymbol = model.GetDeclaredSymbol(classDecl);
if (classSymbol == null)
continue;
// 检查是否有目标特性
if (!classSymbol.GetAttributes().Any(a =>
a.AttributeClass?.Name == "MyAttribute"))
continue;
// 4. 生成代码
string code = GenerateCode(classSymbol);
// 5. 添加生成的源文件
context.AddSource($"{classSymbol.Name}.g.cs", SourceText.From(code, Encoding.UTF8));
}
}
private string GenerateCode(INamedTypeSymbol classSymbol)
{
var sb = new StringBuilder();
sb.AppendLine($"// Generated for {classSymbol.Name}");
sb.AppendLine($"namespace {classSymbol.ContainingNamespace}");
sb.AppendLine("{");
sb.AppendLine($" partial class {classSymbol.Name}");
sb.AppendLine(" {");
sb.AppendLine(" // Generated members");
sb.AppendLine(" }");
sb.AppendLine("}");
return sb.ToString();
}
}
/// <summary>
/// 语法接收器 - 用于在语法遍历过程中收集感兴趣的节点
/// </summary>
class MySyntaxReceiver : ISyntaxReceiver
{
public List<ClassDeclarationSyntax> CandidateClasses { get; } = new();
/// <summary>
/// 对每个语法节点调用
/// 注意:这个方法会被调用数千次,必须非常快
/// </summary>
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
// 只收集带有特性的类
if (syntaxNode is ClassDeclarationSyntax classDecl &&
classDecl.AttributeLists.Count > 0)
{
CandidateClasses.Add(classDecl);
}
}
}关键成员
GeneratorInitializationContext:
csharp
// 注册语法接收器
void RegisterForSyntaxNotifications(SyntaxReceiverCreator creator);
// 注册后初始化回调
void RegisterForPostInitialization(Action<GeneratorPostInitializationContext> callback);GeneratorExecutionContext:
csharp
// 编译信息
Compilation Compilation { get; }
// 语法接收器
ISyntaxReceiver? SyntaxReceiver { get; }
// 取消令牌
CancellationToken CancellationToken { get; }
// 添加源文件
void AddSource(string hintName, SourceText sourceText);
void AddSource(string hintName, string source);
// 报告诊断
void ReportDiagnostic(Diagnostic diagnostic);
// 解析选项
ParseOptions ParseOptions { get; }
// 分析器配置选项
AnalyzerConfigOptionsProvider AnalyzerConfigOptions { get; }
// 附加文件
ImmutableArray<AdditionalText> AdditionalFiles { get; }IIncrementalGenerator (增量生成器 - 推荐)
概述
增量源生成器是 .NET 6 引入的新接口,提供了显著的性能改进。通过管道模式和缓存机制,只在必要时重新生成代码。
优势:
- ⚡ 性能: 比传统生成器快 10-100 倍
- 🔄 增量更新: 只重新生成受影响的部分
- 💡 IDE 支持: 实时反馈,修改代码时立即看到效果
- 🎯 精确缓存: 每个管道阶段都可以被缓存
适用场景:
- 所有生产环境的源生成器
- 需要频繁重新生成的场景
- 大型项目
完整示例
csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Immutable;
using System.Linq;
[Generator]
public class MyIncrementalGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// ============================================================
// 步骤 1: 注册后初始化输出(生成特性定义)
// ============================================================
context.RegisterPostInitializationOutput(ctx => {
ctx.AddSource("MyAttribute.g.cs", @"
using System;
namespace MyNamespace
{
[AttributeUsage(AttributeTargets.Class)]
internal class MyAttribute : Attribute
{
public string Name { get; set; }
}
}");
});
// ============================================================
// 步骤 2: 创建语法提供者管道(两阶段过滤)
// ============================================================
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
// 第一阶段:语法过滤(快速,不需要语义模型)
predicate: static (node, _) => IsSyntaxTargetForGeneration(node),
// 第二阶段:语义转换(精确,使用语义模型)
transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx))
// 过滤掉 null 值
.Where(static m => m is not null);
// ============================================================
// 步骤 3: 注册源输出
// ============================================================
context.RegisterSourceOutput(classDeclarations,
static (spc, classInfo) => Execute(classInfo!, spc));
}
/// <summary>
/// 语法过滤:快速检查语法结构
/// 这个方法会被调用数千次,必须非常快
/// </summary>
static bool IsSyntaxTargetForGeneration(SyntaxNode node)
{
// 只关注带有特性的类
return node is ClassDeclarationSyntax { AttributeLists.Count: > 0 };
}
/// <summary>
/// 语义转换:使用语义模型进行精确验证和数据提取
/// 只对通过语法过滤的节点调用
/// </summary>
static ClassInfo? GetSemanticTargetForGeneration(GeneratorSyntaxContext context)
{
var classDecl = (ClassDeclarationSyntax)context.Node;
var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;
if (classSymbol == null)
return null;
// 检查是否有目标特性
var attribute = classSymbol.GetAttributes()
.FirstOrDefault(a => a.AttributeClass?.Name == "MyAttribute");
if (attribute == null)
return null;
// 提取特性参数
string? name = null;
foreach (var namedArg in attribute.NamedArguments)
{
if (namedArg.Key == "Name" && namedArg.Value.Value is string nameValue)
{
name = nameValue;
}
}
// 返回轻量级数据模型(不要返回 ISymbol 或 SyntaxNode)
return new ClassInfo(
classSymbol.Name,
classSymbol.ContainingNamespace.ToDisplayString(),
name);
}
/// <summary>
/// 生成代码
/// </summary>
static void Execute(ClassInfo classInfo, SourceProductionContext context)
{
var code = $@"
namespace {classInfo.Namespace}
{{
partial class {classInfo.ClassName}
{{
public string GetGeneratedName() => ""{classInfo.AttributeName ?? "Default"}"";
}}
}}";
context.AddSource($"{classInfo.ClassName}.g.cs", code);
}
}
// 轻量级数据模型(使用 record 确保值语义)
record ClassInfo(string ClassName, string Namespace, string? AttributeName);关键成员
IncrementalGeneratorInitializationContext:
csharp
// 语法提供者(用于创建管道)
IncrementalGeneratorSyntaxProvider SyntaxProvider { get; }
// 编译提供者
IncrementalValueProvider<Compilation> CompilationProvider { get; }
// 分析器配置选项提供者
IncrementalValueProvider<AnalyzerConfigOptionsProvider> AnalyzerConfigOptionsProvider { get; }
// 附加文本提供者
IncrementalValuesProvider<AdditionalText> AdditionalTextsProvider { get; }
// 元数据引用提供者
IncrementalValuesProvider<MetadataReference> MetadataReferencesProvider { get; }
// 解析选项提供者
IncrementalValueProvider<ParseOptions> ParseOptionsProvider { get; }
// 注册后初始化输出
void RegisterPostInitializationOutput(Action<IncrementalGeneratorPostInitializationContext> callback);
// 注册源输出
void RegisterSourceOutput<TSource>(
IncrementalValueProvider<TSource> source,
Action<SourceProductionContext, TSource> action);
void RegisterSourceOutput<TSource>(
IncrementalValuesProvider<TSource> source,
Action<SourceProductionContext, TSource> action);SourceProductionContext:
csharp
// 添加源文件
void AddSource(string hintName, SourceText sourceText);
void AddSource(string hintName, string source);
// 报告诊断
void ReportDiagnostic(Diagnostic diagnostic);
// 取消令牌
CancellationToken CancellationToken { get; }ForAttributeWithMetadataName (.NET 7+ 高性能 API)
概述
.NET 7 引入的专门用于基于特性的生成器的高性能 API。相比 CreateSyntaxProvider,它针对特性查找进行了优化。
优势:
- 🚀 更快: 专门为特性查找优化
- 🎯 更简单: 不需要手动编写语法和语义过滤
- 📦 内置缓存: 自动处理增量更新
完整示例
csharp
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 使用 ForAttributeWithMetadataName 查找标记的类
var classDeclarations = context.SyntaxProvider
.ForAttributeWithMetadataName(
// 特性的完全限定名称
fullyQualifiedMetadataName: "MyNamespace.MyAttribute",
// 语法谓词:快速过滤(可选,用于进一步优化)
predicate: static (node, _) => node is ClassDeclarationSyntax,
// 语义转换:提取需要的信息
transform: static (context, _) => GetClassInfo(context))
// 过滤掉 null 结果
.Where(static m => m is not null);
// 注册源输出
context.RegisterSourceOutput(classDeclarations,
static (spc, classInfo) => GenerateCode(classInfo!, spc));
}
static ClassInfo? GetClassInfo(GeneratorAttributeSyntaxContext context)
{
// context.TargetNode: 标记的语法节点
var classDecl = (ClassDeclarationSyntax)context.TargetNode;
// context.TargetSymbol: 标记的符号
var classSymbol = (INamedTypeSymbol)context.TargetSymbol;
// context.Attributes: 匹配的特性(可能有多个)
var attribute = context.Attributes[0];
// context.SemanticModel: 语义模型
var semanticModel = context.SemanticModel;
// 提取信息...
return new ClassInfo(/*...*/);
}关键成员
GeneratorAttributeSyntaxContext:
csharp
// 标记的语法节点
SyntaxNode TargetNode { get; }
// 标记的符号
ISymbol TargetSymbol { get; }
// 匹配的特性(可能有多个,如果类上有多个相同特性)
ImmutableArray<AttributeData> Attributes { get; }
// 语义模型
SemanticModel SemanticModel { get; }API 概览
主要 API 类别
语法树 API - 处理代码的语法结构
SyntaxTree,SyntaxNode,SyntaxToken- 详见: 语法树 API
语义模型 API - 处理代码的语义信息
SemanticModel,ISymbol,ITypeSymbol- 详见: 语义模型 API
编译 API - 管理编译过程
Compilation,MetadataReference- 详见: API 参考手册
代码生成 API - 生成新代码
SyntaxFactory,SourceText- 详见: API 参考手册
诊断 API - 报告错误和警告
Diagnostic,DiagnosticDescriptor- 详见: API 参考手册
💡 关键要点
优先使用增量生成器
IIncrementalGenerator性能远超ISourceGenerator- 支持 IDE 实时反馈
- 自动缓存和增量更新
使用 ForAttributeWithMetadataName (.NET 7+)
- 专门为基于特性的生成器优化
- 比手动过滤快 5-10 倍
- 代码更简洁
两阶段过滤模式
- 第一阶段:语法过滤(快速)
- 第二阶段:语义转换(精确)
- 只对候选节点使用语义模型
使用轻量级数据模型
- 使用
record类型 - 不要传递
SyntaxNode或ISymbol - 实现
IEquatable以支持缓存
- 使用
使用静态方法
- 避免闭包捕获
- 减少内存分配
- 提高性能
🔗 相关文档
📚 下一步
学习完 Roslyn API 介绍后,建议继续学习: