快速参考速查表
常见 Source Generator APIs 和模式的可打印参考
📋 常见任务参考
| 任务 | API / 方法 | 签名 | 链接 |
|---|---|---|---|
| 生成代码 | AddSource() | void AddSource(string, string) | 文档 |
| 查找带特性的类 | ForAttributeWithMetadataName() | IncrementalValuesProvider<T> | 文档 |
| 获取语义模型 | GetSemanticModel() | SemanticModel GetSemanticModel(SyntaxTree) | 文档 |
| 获取类符号 | GetDeclaredSymbol() | ISymbol GetDeclaredSymbol(SyntaxNode) | 文档 |
| 报告诊断 | ReportDiagnostic() | void ReportDiagnostic(Diagnostic) | 文档 |
| 查找所有类 | GetSymbolsWithName() | IEnumerable<ISymbol> | 文档 |
| 获取属性 | GetMembers() | ImmutableArray<ISymbol> | 文档 |
| 解析代码 | ParseText() | SyntaxTree ParseText(string) | 文档 |
| 创建语法节点 | SyntaxFactory.* | 各种 | 文档 |
| 获取特性 | GetAttributes() | ImmutableArray<AttributeData> | 文档 |
🔧 生成器接口
ISourceGenerator(旧版)
csharp
[Generator]
public class MyGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context) { }
public void Execute(GeneratorExecutionContext context)
{
// 生成代码
context.AddSource("File.g.cs", sourceCode);
}
}IIncrementalGenerator(推荐)⭐
csharp
[Generator]
public class MyGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var provider = context.SyntaxProvider.CreateSyntaxProvider(/*...*/);
context.RegisterSourceOutput(provider, Generate);
}
}🔍 查找代码元素
查找带特性的类
csharp
context.SyntaxProvider.ForAttributeWithMetadataName(
"MyAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => GetInfo(ctx))查找所有类
csharp
compilation.GetSymbolsWithName(
name => true,
SymbolFilter.Type)
.OfType<INamedTypeSymbol>()
.Where(t => t.TypeKind == TypeKind.Class)获取类成员
csharp
// 属性
classSymbol.GetMembers().OfType<IPropertySymbol>()
// 方法
classSymbol.GetMembers().OfType<IMethodSymbol>()
// 字段
classSymbol.GetMembers().OfType<IFieldSymbol>()
// 所有公共成员
classSymbol.GetMembers()
.Where(m => m.DeclaredAccessibility == Accessibility.Public)🔨 代码生成
基于字符串的生成
csharp
var code = $"""
namespace {ns}
{
public partial class {className}
{
public string Property { get; set; }
}
}
""";
context.AddSource($"{className}.g.cs", code);SyntaxFactory 生成
csharp
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
var classDecl = ClassDeclaration(className)
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddMembers(/* 成员 */);
var code = classDecl.NormalizeWhitespace().ToFullString();⚠️ 诊断
创建诊断描述符
csharp
private static readonly DiagnosticDescriptor Rule = new(
id: "GEN001",
title: "错误标题",
messageFormat: "错误:{0}",
category: "Generator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);报告诊断
csharp
var diagnostic = Diagnostic.Create(
Rule,
location,
messageArgs);
context.ReportDiagnostic(diagnostic);严重性级别
DiagnosticSeverity.Error- 停止编译DiagnosticSeverity.Warning- 显示警告DiagnosticSeverity.Info- 信息性DiagnosticSeverity.Hidden- 不显示
🎯 语义模型
从语法获取符号
csharp
var semanticModel = compilation.GetSemanticModel(syntaxTree);
var symbol = semanticModel.GetDeclaredSymbol(syntaxNode);获取类型信息
csharp
var typeInfo = semanticModel.GetTypeInfo(expression);
var type = typeInfo.Type; // ITypeSymbol符号属性
csharp
symbol.Name // 符号名称
symbol.ContainingNamespace // 命名空间
symbol.DeclaredAccessibility // public、private 等
symbol.IsStatic // Static 修饰符
symbol.IsAbstract // Abstract 修饰符📊 类型检查
检查接口实现
csharp
typeSymbol.AllInterfaces.Any(i =>
i.ToDisplayString() == "System.IDisposable")检查基类
csharp
var baseType = typeSymbol.BaseType;
while (baseType != null)
{
if (baseType.ToDisplayString() == "MyBaseClass")
return true;
baseType = baseType.BaseType;
}检查类型种类
csharp
typeSymbol.TypeKind == TypeKind.Class
typeSymbol.TypeKind == TypeKind.Interface
typeSymbol.TypeKind == TypeKind.Struct
typeSymbol.TypeKind == TypeKind.Enum🚀 增量管道
基本管道
csharp
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 1. 创建提供程序
var classes = context.SyntaxProvider.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => Transform(ctx));
// 2. 注册输出
context.RegisterSourceOutput(classes, Generate);
}组合数据源
csharp
var combined = context.CompilationProvider
.Combine(classes.Collect());
context.RegisterSourceOutput(combined,
(spc, source) => Generate(spc, source.Left, source.Right));过滤和转换
csharp
var filtered = provider
.Where(item => item != null)
.Select((item, _) => Transform(item));📦 常见模式
检查类是否为 Partial
csharp
bool isPartial = classDecl.Modifiers
.Any(m => m.IsKind(SyntaxKind.PartialKeyword));获取特性参数
csharp
var attribute = symbol.GetAttributes()
.FirstOrDefault(a => a.AttributeClass?.Name == "MyAttribute");
// 构造函数参数
var arg1 = attribute.ConstructorArguments[0].Value;
// 命名参数
var namedArg = attribute.NamedArguments
.FirstOrDefault(a => a.Key == "PropertyName").Value.Value;生成唯一文件名
csharp
var fileName = $"{className}_{Guid.NewGuid():N}.g.cs";
// 或
var fileName = $"{namespaceName}.{className}.g.cs"
.Replace(".", "_");🔗 有用的扩展
符号扩展
csharp
// 获取带命名空间的完整名称
typeSymbol.ToDisplayString()
// 获取所有基类型和接口
typeSymbol.AllInterfaces
typeSymbol.BaseType
// 检查可访问性
symbol.DeclaredAccessibility == Accessibility.Public语法扩展
csharp
// 获取父节点
node.Parent
// 查找类型的祖先
node.FirstAncestorOrSelf<ClassDeclarationSyntax>()
// 获取所有后代
node.DescendantNodes()💡 最佳实践
✅ 应该做
- 新项目使用
IIncrementalGenerator - 在管道早期过滤(在
predicate中) - 对特性使用
ForAttributeWithMetadataName() - 将目标类设为
partial - 向生成的代码添加
[GeneratedCode]特性 - 使用有意义的诊断 ID(例如 "GEN001")
❌ 不应该做
- 不要在新代码中使用
ISourceGenerator - 不要在
transform后过滤(在predicate中过滤) - 不要忽略错误处理
- 不要在没有验证的情况下生成代码
- 不要在
predicate中使用昂贵的操作 - 不要忘记使用 SyntaxFactory 规范化空白
📚 API 命名空间
csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using System.Collections.Immutable;🔢 常见类型映射
| C# 类型 | Roslyn 类型 | SyntaxFactory |
|---|---|---|
int | PredefinedType | PredefinedType(Token(SyntaxKind.IntKeyword)) |
string | PredefinedType | PredefinedType(Token(SyntaxKind.StringKeyword)) |
bool | PredefinedType | PredefinedType(Token(SyntaxKind.BoolKeyword)) |
void | PredefinedType | PredefinedType(Token(SyntaxKind.VoidKeyword)) |
object | PredefinedType | PredefinedType(Token(SyntaxKind.ObjectKeyword)) |
MyClass | IdentifierName | IdentifierName("MyClass") |
List<T> | GenericName | GenericName("List").WithTypeArgumentList(...) |
🎨 修饰符
csharp
// 常见修饰符
Token(SyntaxKind.PublicKeyword)
Token(SyntaxKind.PrivateKeyword)
Token(SyntaxKind.ProtectedKeyword)
Token(SyntaxKind.InternalKeyword)
Token(SyntaxKind.StaticKeyword)
Token(SyntaxKind.ReadOnlyKeyword)
Token(SyntaxKind.PartialKeyword)
Token(SyntaxKind.AbstractKeyword)
Token(SyntaxKind.VirtualKeyword)
Token(SyntaxKind.OverrideKeyword)
Token(SyntaxKind.SealedKeyword)
Token(SyntaxKind.AsyncKeyword)📖 快速链接
📄 打印提示
为获得最佳打印效果:
- 使用横向方向
- 将边距设置为 0.5 英寸
- 启用背景图形以提高可读性
- 考虑双面打印以节省纸张
版本: 1.0 | 最后更新: 2024 | 在线查看