增量源代码生成器如何获取符号(含依赖)的所有实现?
在增量源代码生成器中查找接口的所有实现(含依赖程序集)
核心思路
源代码生成器环境下无法直接使用独立Roslyn环境的SymbolFinder方法,但可以通过Compilation对象结合符号遍历、匹配逻辑实现需求,且天然支持访问依赖程序集中的符号。
具体实现步骤
1. 获取编译上下文
在生成器初始化阶段,通过CompilationProvider拿到当前编译的入口对象Compilation,它包含了当前项目及所有依赖程序集的符号信息:
context.RegisterSourceOutput(context.CompilationProvider, (spc, compilation) => { // 后续逻辑基于此compilation对象展开 });
2. 遍历并匹配接口实现
通过全局命名空间递归遍历所有可见类型,检查类型是否实现了目标接口(含间接实现):
// 先获取目标接口的符号 var targetInterface = compilation.GetTypeByMetadataName("Your.Namespace.IYourInterface"); if (targetInterface == null) return; var implementations = new List<INamedTypeSymbol>(); TraverseNamespace(compilation.GlobalNamespace, targetInterface, implementations); // 递归遍历命名空间的方法 void TraverseNamespace(INamespaceSymbol ns, INamedTypeSymbol targetInterface, List<INamedTypeSymbol> result) { foreach (var member in ns.GetMembers()) { if (member is INamespaceSymbol nestedNs) { TraverseNamespace(nestedNs, targetInterface, result); } else if (member is INamedTypeSymbol type) { // 用SymbolEqualityComparer确保符号匹配的准确性 if (type.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, targetInterface))) { result.Add(type); } } } }
3. 增量优化(适配Incremental Source Generator)
如果需要提升性能,避免每次全量遍历,可结合SyntaxProvider筛选可能的候选类型,再做符号验证:
var interfaceImplementations = context.SyntaxProvider .CreateSyntaxProvider( // 先筛选语法节点:只处理类型声明 predicate: (node, _) => node is TypeDeclarationSyntax, // 转换为符号并验证是否实现目标接口 transform: (ctx, _) => { var typeSymbol = ctx.SemanticModel.GetDeclaredSymbol(ctx.Node) as INamedTypeSymbol; var targetInterface = ctx.SemanticModel.Compilation.GetTypeByMetadataName("Your.Namespace.IYourInterface"); return typeSymbol != null && typeSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, targetInterface)) ? typeSymbol : null; }) .Where(type => type != null); // 注册输出逻辑 context.RegisterSourceOutput(interfaceImplementations, (spc, type) => { // 生成对应代码 });
关键注意事项
- 必须使用
SymbolEqualityComparer.Default比较符号,不能直接用==,否则会因符号实例不同导致匹配失败。 - 处理泛型接口时,需额外检查
ConstructedFrom属性是否等于目标接口的原始定义。 - 若目标接口在当前项目定义,也可先通过
SyntaxProvider定位到接口符号,再进行后续匹配。
内容的提问来源于stack exchange,提问作者user3797758
相关产品推荐
相关产品推荐

