Skip to content

快速参考速查表

常见 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
intPredefinedTypePredefinedType(Token(SyntaxKind.IntKeyword))
stringPredefinedTypePredefinedType(Token(SyntaxKind.StringKeyword))
boolPredefinedTypePredefinedType(Token(SyntaxKind.BoolKeyword))
voidPredefinedTypePredefinedType(Token(SyntaxKind.VoidKeyword))
objectPredefinedTypePredefinedType(Token(SyntaxKind.ObjectKeyword))
MyClassIdentifierNameIdentifierName("MyClass")
List<T>GenericNameGenericName("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 | 在线查看

基于 MIT 许可发布