使用 Roslyn 从编译中获取所有类型?

dnf*_*dnf 3 c# roslyn .net-core

我试图根据以下代码创建的 Roslyn 编译中的某些标准获取所有类型:

var syntaxTrees = new[] 
{ 
      CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.Preview)) 
};

// Add some references
references.Add(MetadataReference.CreateFromFile(...));
            
// Create compilation
var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
var compilation = CSharpCompilation.Create(nameof(GeneratorRunner), syntaxTrees, references, options);

return compilation;
Run Code Online (Sandbox Code Playgroud)

然后我使用这段代码来搜索类型

compilation.GetTypeByMetadataName("HomeCenter.Messages.Commands.Device.AdjustPowerLevelCommand");
compilation.GetSymbolsWithName(x => true, SymbolFilter.Type).ToList();
Run Code Online (Sandbox Code Playgroud)

这对我来说很奇怪 - GetSymbolsWithName 仅返回在我的源中定义的类型,而不是在引用的程序集中定义的类型,但 GetTypeByMetadataName 能够返回有关类型的信息,即使它是在引用程序集中定义的。问题是我正在尝试搜索编译中的所有类型,所以我不知道确切的名称。问题是如何搜索应用程序在源中以及引用的程序集中具有的所有类型?还有其他与名称相反的过滤选项吗 - 我对从特定类型继承的所有类型感兴趣,以便可以以各种方式命名它们。

Jas*_*ski 7

如果您想开始搜索所有类型,您可以执行 Compilation.GlobalNamespace,它为您提供一个 INamespaceSymbol ,表示层次结构的“根”命名空间。从那里您可以调用 GetTypeMembers() 来获取该命名空间中的类型,并调用 GetNamespaceMembers() 来获取子命名空间。您必须自己进行递归,我认为我们没有帮助者。


SEN*_*Nya 6

只是对 Jason Malinowski 的回答的补充。您可以使用以下符号访问者帮助程序:

internal class ExportedTypesCollector : SymbolVisitor
{
    private readonly CancellationToken _cancellationToken;
    private readonly HashSet<INamedTypeSymbol> _exportedTypes;

    public ExportedTypesCollector(CancellationToken cancellation, int? estimatedCapacity = null)
    { 
        _cancellationToken = cancellation;
        _exportedTypes = estimatedCapacity.HasValue
            ? new HashSet<INamedTypeSymbol>(estimatedCapacity.Value, SymbolEqualityComparer.Default)
            : new HashSet<INamedTypeSymbol>(SymbolEqualityComparer.Default);
    }

    public ImmutableArray<INamedTypeSymbol> GetPublicTypes() => _exportedTypes.ToImmutableArray();

    public override void VisitAssembly(IAssemblySymbol symbol)
    {
        _cancellationToken.ThrowIfCancellationRequested();
        symbol.GlobalNamespace.Accept(this);
    }

    public override void VisitNamespace(INamespaceSymbol symbol)
    {
        foreach (INamespaceOrTypeSymbol namespaceOrType in symbol.GetMembers())
        {
            _cancellationToken.ThrowIfCancellationRequested();
            namespaceOrType.Accept(this);
        }
    }

    public override void VisitNamedType(INamedTypeSymbol type)
    {
        _cancellationToken.ThrowIfCancellationRequested();

        if (!type.IsAccessibleOutsideOfAssembly() || !_exportedTypes.Add(type))
            return;

        var nestedTypes = type.GetTypeMembers();

        if (nestedTypes.IsDefaultOrEmpty)
            return;

        foreach (INamedTypeSymbol nestedType in nestedTypes)
        {
            _cancellationToken.ThrowIfCancellationRequested();
            nestedType.Accept(this);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

IsAccessibleOutsideOfAssembly使用这样的扩展方法:

public static bool IsAccessibleOutsideOfAssembly(this ISymbol symbol) =>
        symbol.DeclaredAccessibility switch
        {
            Accessibility.Private => false,
            Accessibility.Internal => false,
            Accessibility.ProtectedAndInternal => false,
            Accessibility.Protected => true,
            Accessibility.ProtectedOrInternal => true,
            Accessibility.Public => true,
            _ => true,    //Here should be some reasonable default
        };
Run Code Online (Sandbox Code Playgroud)