如何在C#源代码生成器中查找指定类的所有派生类?
在C#源代码生成器中查找所有派生类的实现方案
问题描述
在C#源代码生成器中,需要查找所有继承自目标类的其他类,以此判断是否需要添加特殊代码。目前仅能处理目标类本身及其属性类型等引用,无法检索到项目中的其他类,期望实现类似如下功能:
public static IEnumerable<string> GetDerivedTypes(this ITypeSymbol typeSymbol) { // TODO: Find all available classes, // then I can proceed with inheritance checks and further tests // Following is made-up code: var derivedTypeNames = typeSymbol.ContainingAssembly.AllTypes .Where(t => t.IsDerivedFrom(typeSymbol)) .Select(t => t.Name); return derivedTypeNames; }
实现方案
这一需求完全可行,核心是利用Compilation对象遍历整个编译环境中的所有类型,并通过符号比较判断继承关系。以下是具体实现步骤及代码:
1. 实现类型继承判断的扩展方法
首先需要一个辅助方法来判断一个类型是否派生自目标类型,覆盖类继承、接口实现及泛型类型的场景:
using Microsoft.CodeAnalysis; using System.Collections.Generic; using System.Linq; public static class TypeSymbolExtensions { public static bool IsDerivedFrom(this ITypeSymbol typeSymbol, ITypeSymbol baseTypeSymbol) { if (typeSymbol == null || baseTypeSymbol == null) return false; var equalityComparer = SymbolEqualityComparer.Default; var targetBaseType = baseTypeSymbol.OriginalDefinition; // 排除类型自身 if (equalityComparer.Equals(typeSymbol.OriginalDefinition, targetBaseType)) return false; // 遍历类继承链 var currentBaseType = typeSymbol.BaseType; while (currentBaseType != null) { if (equalityComparer.Equals(currentBaseType.OriginalDefinition, targetBaseType)) return true; currentBaseType = currentBaseType.BaseType; } // 检查接口实现 if (targetBaseType.TypeKind == TypeKind.Interface) { foreach (var implementedInterface in typeSymbol.AllInterfaces) { if (equalityComparer.Equals(implementedInterface.OriginalDefinition, targetBaseType)) return true; } } return false; } }
2. 递归遍历所有类型的辅助方法
需要一个方法递归遍历命名空间下的所有类型(包括嵌套类型):
public static IEnumerable<INamedTypeSymbol> GetAllTypes(this INamespaceSymbol namespaceSymbol) { foreach (var member in namespaceSymbol.GetMembers()) { if (member is INamedTypeSymbol namedType) { yield return namedType; // 遍历嵌套类型 foreach (var nestedType in namedType.GetAllTypes()) { yield return nestedType; } } else if (member is INamespaceSymbol nestedNamespace) { foreach (var type in nestedNamespace.GetAllTypes()) { yield return type; } } } }
3. 实现获取派生类的核心方法
通过Compilation对象遍历所有类型,筛选出派生自目标类型的类:
public static IEnumerable<string> GetDerivedTypes(this ITypeSymbol typeSymbol, Compilation compilation) { if (typeSymbol == null || compilation == null) yield break; foreach (var namedType in compilation.GlobalNamespace.GetAllTypes()) { if (namedType.IsDerivedFrom(typeSymbol)) { yield return namedType.Name; } } }
4. 在源代码生成器中使用
在生成器的Execute方法中,通过GeneratorExecutionContext获取Compilation,然后调用上述方法:
public void Execute(GeneratorExecutionContext context) { var compilation = context.Compilation; // 假设已获取到目标类型符号typeSymbol var derivedTypeNames = typeSymbol.GetDerivedTypes(compilation); // 后续处理逻辑,例如根据派生类存在与否生成特殊代码 }
注意事项
- 性能优化:遍历所有类型在大型项目中可能影响性能,可考虑缓存结果,或仅在目标类型发生变化时执行。
- 范围控制:若仅需当前项目的派生类,可在筛选时添加条件
namedType.ContainingAssembly.Equals(typeSymbol.ContainingAssembly, SymbolEqualityComparer.Default)。 - 泛型处理:上述方法已通过
OriginalDefinition处理泛型类型的比较,支持判断List<int>是否派生自List<T>这类场景。
内容的提问来源于stack exchange,提问作者ygoe
相关产品推荐
相关产品推荐

