You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 01:00:11