任务索引:我想要...
按照你想要完成的任务查找常见 Source Generator 任务的解决方案
此索引帮助你快速找到适合你任务的 API。每个条目都包含可用的代码示例和详细文档的链接。
🔍 查找和分析
查找项目中的所有类
推荐: 使用 Compilation.GetSymbolsWithName() ⭐
csharp
var allClasses = compilation
.GetSymbolsWithName(name => true, SymbolFilter.Type)
.OfType<INamedTypeSymbol>()
.Where(t => t.TypeKind == TypeKind.Class);
foreach (var classSymbol in allClasses)
{
var className = classSymbol.Name;
var namespaceName = classSymbol.ContainingNamespace.ToDisplayString();
// 处理类...
}查找带有特定特性的类
最佳方法: 使用 ForAttributeWithMetadataName() ⭐
csharp
var classesWithAttribute = context.SyntaxProvider
.ForAttributeWithMetadataName(
"MyNamespace.MyAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => GetClassInfo(ctx));
static ClassInfo GetClassInfo(GeneratorAttributeSyntaxContext context)
{
var classDecl = (ClassDeclarationSyntax)context.TargetNode;
var classSymbol = (INamedTypeSymbol)context.TargetSymbol;
return new ClassInfo(classSymbol.Name, classSymbol.ContainingNamespace.ToDisplayString());
}替代方法: 手动过滤(较慢)
csharp
var classesWithAttribute = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax cls &&
cls.AttributeLists.Count > 0,
transform: (ctx, _) => GetClassWithAttribute(ctx))
.Where(c => c is not null);查找类的所有属性
csharp
var properties = classSymbol.GetMembers()
.OfType<IPropertySymbol>()
.Where(p => p.DeclaredAccessibility == Accessibility.Public);
foreach (var property in properties)
{
var propName = property.Name;
var propType = property.Type.ToDisplayString();
var isReadOnly = property.SetMethod == null;
// 处理属性...
}查找类中的所有方法
csharp
var methods = classSymbol.GetMembers()
.OfType<IMethodSymbol>()
.Where(m => m.MethodKind == MethodKind.Ordinary); // 排除构造函数等
foreach (var method in methods)
{
var methodName = method.Name;
var returnType = method.ReturnType.ToDisplayString();
var parameters = method.Parameters;
// 处理方法...
}获取类上的所有特性
csharp
var attributes = classSymbol.GetAttributes();
foreach (var attribute in attributes)
{
var attributeName = attribute.AttributeClass?.Name;
// 获取构造函数参数
foreach (var arg in attribute.ConstructorArguments)
{
var value = arg.Value;
}
// 获取命名参数
foreach (var namedArg in attribute.NamedArguments)
{
var name = namedArg.Key;
var value = namedArg.Value.Value;
}
}检查类型是否实现接口
csharp
bool ImplementsInterface(INamedTypeSymbol typeSymbol, string interfaceName)
{
return typeSymbol.AllInterfaces.Any(i =>
i.ToDisplayString() == interfaceName);
}
// 使用
if (ImplementsInterface(classSymbol, "System.IDisposable"))
{
// 类实现了 IDisposable
}检查类型是否继承自基类
csharp
bool InheritsFrom(INamedTypeSymbol typeSymbol, string baseClassName)
{
var current = typeSymbol.BaseType;
while (current != null)
{
if (current.ToDisplayString() == baseClassName)
return true;
current = current.BaseType;
}
return false;
}获取类型的命名空间
csharp
var namespaceName = typeSymbol.ContainingNamespace.ToDisplayString();
// 检查是否在全局命名空间中
if (typeSymbol.ContainingNamespace.IsGlobalNamespace)
{
// 类型在全局命名空间中
}🔨 生成代码
生成简单的类
推荐: 对于简单代码使用基于字符串的生成 ⭐
csharp
var code = $"""
namespace {namespaceName}
{
public partial class {className}
{
public string GeneratedProperty { get; set; }
public void GeneratedMethod()
{
// 生成的代码
}
}
}
""";
context.AddSource($"{className}.g.cs", code);生成带属性的类
csharp
var properties = new[] { ("Name", "string"), ("Age", "int"), ("Email", "string") };
var propertiesCode = string.Join("\n ", properties.Select(p =>
$"public {p.Item2} {p.Item1} {{ get; set; }}"));
var code = $"""
namespace {namespaceName}
{
public partial class {className}
{
{propertiesCode}
}
}
""";
context.AddSource($"{className}.g.cs", code);生成方法
csharp
var methodCode = $"""
public {returnType} {methodName}({parameters})
{
{methodBody}
}
""";生成 using 语句
csharp
var usings = new[] { "System", "System.Collections.Generic", "System.Linq" };
var usingStatements = string.Join("\n", usings.Select(u => $"using {u};"));
var code = $"""
{usingStatements}
namespace {namespaceName}
{
public partial class {className}
{
// 类内容
}
}
""";在类上生成特性
csharp
var code = $"""
namespace {namespaceName}
{
[System.CodeDom.Compiler.GeneratedCode("MyGenerator", "1.0")]
[System.Diagnostics.DebuggerNonUserCode]
public partial class {className}
{
// 类内容
}
}
""";生成 XML 文档注释
csharp
var code = $"""
namespace {namespaceName}
{
/// <summary>
/// 为 {originalClassName} 生成的类
/// </summary>
/// <remarks>
/// 此类是自动生成的。请勿修改。
/// </remarks>
public partial class {className}
{
/// <summary>
/// 获取或设置名称
/// </summary>
public string Name { get; set; }
}
}
""";使用 SyntaxFactory 生成代码(复杂结构)
何时使用: 对于难以用字符串管理的复杂代码结构
csharp
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
var classDeclaration = ClassDeclaration(className)
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.PartialKeyword))
.AddMembers(
PropertyDeclaration(
PredefinedType(Token(SyntaxKind.StringKeyword)),
"Name")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddAccessorListAccessors(
AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))));
var namespaceDecl = NamespaceDeclaration(ParseName(namespaceName))
.AddMembers(classDeclaration);
var code = namespaceDecl.NormalizeWhitespace().ToFullString();
context.AddSource($"{className}.g.cs", code);生成多个文件
csharp
foreach (var classInfo in classesToGenerate)
{
var code = GenerateCodeForClass(classInfo);
context.AddSource($"{classInfo.Name}.g.cs", code);
}格式化生成的代码
csharp
// 使用原始字符串字面量并正确缩进
var code = $"""
namespace {namespaceName}
{
public partial class {className}
{
public void Method()
{
// 正确缩进
}
}
}
""";
// 或使用 SyntaxFactory 配合 NormalizeWhitespace()
var formatted = syntaxNode.NormalizeWhitespace().ToFullString();⚠️ 报告问题
报告错误
csharp
private static readonly DiagnosticDescriptor ErrorRule = new(
id: "GEN001",
title: "无效配置",
messageFormat: "类 '{0}' 的配置无效:{1}",
category: "Generator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);
// 报告错误
var diagnostic = Diagnostic.Create(
ErrorRule,
location,
className,
errorMessage);
context.ReportDiagnostic(diagnostic);报告警告
csharp
private static readonly DiagnosticDescriptor WarningRule = new(
id: "GEN002",
title: "潜在问题",
messageFormat: "类 '{0}' 可能存在问题:{1}",
category: "Generator",
DiagnosticSeverity.Warning,
isEnabledByDefault: true);
context.ReportDiagnostic(Diagnostic.Create(WarningRule, location, className, warning));报告信息消息
csharp
private static readonly DiagnosticDescriptor InfoRule = new(
id: "GEN003",
title: "信息",
messageFormat: "为 '{0}' 生成了代码",
category: "Generator",
DiagnosticSeverity.Info,
isEnabledByDefault: true);
context.ReportDiagnostic(Diagnostic.Create(InfoRule, Location.None, className));报告带有多个位置的诊断
csharp
var diagnostic = Diagnostic.Create(
descriptor,
primaryLocation,
additionalLocations: new[] { location1, location2 },
messageArgs: new[] { "arg1", "arg2" });
context.ReportDiagnostic(diagnostic);创建自定义诊断严重性
csharp
// Error - 停止编译
DiagnosticSeverity.Error
// Warning - 显示警告但继续编译
DiagnosticSeverity.Warning
// Info - 仅供参考
DiagnosticSeverity.Info
// Hidden - 不显示给用户
DiagnosticSeverity.Hidden🚀 性能优化
使用增量生成器
新项目始终使用 ⭐
csharp
[Generator]
public class MyIncrementalGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 带缓存的增量管道
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => (ClassDeclarationSyntax)ctx.Node);
context.RegisterSourceOutput(classDeclarations,
(spc, classDecl) => GenerateCode(spc, classDecl));
}
}缓存昂贵的计算
csharp
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 步骤 1:查找类(已缓存)
var classes = context.SyntaxProvider
.CreateSyntaxProvider(/* ... */);
// 步骤 2:提取信息(每个类缓存)
var classInfos = classes.Select((cls, _) => ExtractInfo(cls));
// 步骤 3:与编译组合(仅在需要时)
var combined = context.CompilationProvider.Combine(classInfos.Collect());
// 步骤 4:生成(仅在输入更改时)
context.RegisterSourceOutput(combined, (spc, source) => Generate(spc, source));
}在管道早期过滤
推荐: 尽早过滤 ⭐
csharp
// 好:在 predicate 中过滤(快速)
var classes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax cls &&
cls.Modifiers.Any(SyntaxKind.PartialKeyword),
transform: (ctx, _) => ctx.Node);
// 差:在 transform 后过滤(慢)
var classes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => ctx.Node)
.Where(cls => ((ClassDeclarationSyntax)cls).Modifiers.Any(SyntaxKind.PartialKeyword));避免不必要的语义分析
csharp
// 好:尽可能使用仅语法过滤
var classes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax cls &&
cls.AttributeLists.Count > 0, // 语法检查
transform: (ctx, _) => GetSemanticInfo(ctx)); // 仅在需要时使用语义
// 差:总是使用语义模型
var classes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => true, // 无过滤
transform: (ctx, _) => GetSemanticInfo(ctx)); // 对所有节点都昂贵🔧 使用编译
访问编译
csharp
public void Execute(GeneratorExecutionContext context)
{
var compilation = context.Compilation;
// 使用编译获取语义信息
var syntaxTree = compilation.SyntaxTrees.First();
var semanticModel = compilation.GetSemanticModel(syntaxTree);
}添加元数据引用
csharp
var references = new[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location)
};
var compilation = CSharpCompilation.Create(
"MyAssembly",
syntaxTrees: new[] { syntaxTree },
references: references);获取所有语法树
csharp
foreach (var syntaxTree in compilation.SyntaxTrees)
{
var root = syntaxTree.GetRoot();
var filePath = syntaxTree.FilePath;
// 处理语法树...
}