如何在ISyntaxContextReceiver中对比生成类型,检测类标记的指定特性
解决方案
核心思路
不要手动对比零散的名称字符串,用Roslyn提供的SymbolEqualityComparer做类型符号全等对比,可覆盖别名、嵌套、同名不同命名空间等所有场景,不会出现误判。
具体实现步骤
步骤1:在生成器Execute阶段预获取目标特性的类型符号
在你第二个需要检测特性的生成器的Execute方法中,先从编译上下文拿到你生成的目标特性的ITypeSymbol:
public void Execute(GeneratorExecutionContext context) { // 注意元数据名称要写全,带Attribute后缀、完整命名空间 var targetAttributeType = context.Compilation.GetTypeByMetadataName("DependencyReproGenerator.Library.Attributes.CustomAttribute1Attribute"); if (targetAttributeType == null) { // 未找到目标特性,说明当前项目没有引用生成特性的生成器,直接返回 return; } // 拿到Receiver里收集的类声明 if (context.SyntaxContextReceiver is not ConstructorGeneratorReceiver receiver) return; // 后续处理逻辑 }
步骤2:优化SyntaxReceiver的判断逻辑
不需要手动解析Attribute的语法Token,直接拿到类的类型符号后遍历其特性列表,用符号比较器判断:
class ConstructorGeneratorReceiver : ISyntaxContextReceiver { public List<INamedTypeSymbol> MarkedClasses { get; } = new(); public void OnVisitSyntaxNode(GeneratorSyntaxContext context) { if (context.Node is not ClassDeclarationSyntax classDec) return; // 拿到当前类的类型符号 if (context.SemanticModel.GetDeclaredSymbol(classDec) is not INamedTypeSymbol classSymbol) return; // 遍历类的所有特性 foreach (var attributeData in classSymbol.GetAttributes()) { // 特性类型为空直接跳过 if (attributeData.AttributeClass == null) continue; // 这里先临时用完整名称对比,等Execute阶段拿到目标类型后也可以用SymbolEqualityComparer.Default.Equals做二次过滤,准确性更高 if (attributeData.AttributeClass.ToDisplayString() == "DependencyReproGenerator.Library.Attributes.CustomAttribute1Attribute") { MarkedClasses.Add(classSymbol); break; } } } }
可选:直接在Receiver中做精确对比的简化写法
如果你不想在Execute阶段二次过滤,也可以直接在遍历Attribute时拿到特性类型符号后对比名称+命名空间:
private bool IsTargetAttribute(ITypeSymbol? attributeType) { if (attributeType == null) return false; return attributeType.Name == "CustomAttribute1Attribute" && attributeType.ContainingNamespace.ToDisplayString() == "DependencyReproGenerator.Library.Attributes"; }
注意事项
- 不要省略特性的
Attribute后缀,元数据中存储的类型名是完整带后缀的,用GetTypeByMetadataName时必须写全 SymbolEqualityComparer.Default.Equals是官方提供的类型符号全等判断方法,比手动拼接字符串更可靠,可处理类型重定向、别名引用等特殊场景- 同一个解决方案中多个源生成器的输出会被合并到编译上下文,只要生成特性的生成器被项目正确引用,就能通过
GetTypeByMetadataName拿到对应的类型符号
内容的提问来源于stack exchange,提问作者Jamie Burns
相关产品推荐
相关产品推荐

