简体   繁体   English

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

[英]Get all types from compilation using Roslyn?

I'm trying to get all types by some criteria from Roslyn Compilation created by this code:我正在尝试通过此代码创建的 Roslyn Compilation 的某些标准获取所有类型:

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;

Then I'm using this code to seach for types然后我使用此代码来搜索类型

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

This works weird for me - GetSymbolsWithName returns only types defined in my sources but not in referenced assemblies but GetTypeByMetadataName is able to return information about type even when it is defined in reference assembly.这对我来说很奇怪 - GetSymbolsWithName 只返回在我的源中定义的类型,而不是在引用的程序集中,但 GetTypeByMetadataName 能够返回有关类型的信息,即使它是在引用程序集中定义的。 The problem is I'm trying to search for all types in compilation so I don't know exact names.问题是我试图在编译中搜索所有类型,所以我不知道确切的名称。 The question is how can I search in all types application have in sources but also in referenced assemblies?问题是如何搜索应用程序在源和引用程序集中的所有类型? Also is there any other option for filtering opposite to name - I'm interested in all types that inherit from specific type so they can be named in all kind of ways.还有任何其他选项可以过滤与名称相反的过滤器 - 我对从特定类型继承的所有类型感兴趣,因此它们可以以各种方式命名。

If you want to start searching through all types, you can do Compilation.GlobalNamespace, which gives you a INamespaceSymbol that represents the "root" namespace of the hierarchy.如果您想开始搜索所有类型,您可以执行 Compilation.GlobalNamespace,它为您提供一个 INamespaceSymbol,表示层次结构的“根”命名空间。 From there you can call GetTypeMembers() to get types that are in that namespace, and GetNamespaceMembers() to get child namespaces.从那里你可以调用 GetTypeMembers() 来获取该命名空间中的类型,并调用 GetNamespaceMembers() 来获取子命名空间。 You'll have to do the recursion yourself, I don't think we have a helper for that.您必须自己进行递归,我认为我们没有帮助。

Just addition to the answer from Jason Malinowski.只是对 Jason Malinowski 的回答的补充。 You can use the following symbol visitor helper:您可以使用以下符号访问者助手:

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);
        }
    }
}

with an extension method IsAccessibleOutsideOfAssembly like this:使用扩展方法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
        };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM