【问题标题】:Get all types from compilation using Roslyn?使用 Roslyn 从编译中获取所有类型?
【发布时间】:2020-10-31 16:00:04
【问题描述】:

我正在尝试通过此代码创建的 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;

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

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

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

【问题讨论】:

    标签: c# .net-core roslyn


    【解决方案1】:

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

    【讨论】:

    • 好的,我可以找到我想要的所有信息。谢谢,
    【解决方案2】:

    只是对 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);
            }
        }
    }
    

    使用扩展方法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
            };
    

    【讨论】:

    • 这位访客很棒,但我不明白一件事。在方法 VisitNamedType 中有 type.GetTypeMembers()。当 type 是一个类时,它对我来说很奇怪 - 它返回所有方法,就像 TypeKind = class?我认为 GetTypeMembers() 将返回嵌套类,但它是不同的?
    • 我检查了一下,它为我返回了嵌套类型。你描述了一个非常奇怪的行为。它应该返回嵌套类型,我从未见过它与类型的工作方式不同。但是,也许您的代码中有委托声明?委托类型也是类型,也会被返回。
    • 当您运行此测试github.com/dominikjeske/HomeCenter/blob/MotionService/… 并在此处点击断点github.com/dominikjeske/HomeCenter/blob/MotionService/… 时,例如 HomeCenter.SourceGenerators.Tests.MessageGenerator.d__0 这是 TypeKind 类。但这不是唯一的。
    • 很抱歉,我还没有安装 .net 5 并使用它。我无法在您的存储库中构建 MotionService 分支并调试测试。将依赖项更改为 .net core 3.1 没有成功。我不会解决依赖问题,因为我会花很多时间。但是,如果您可以创建一个包含有问题的代码并且不依赖于 .net 5 的分支,那么我可以尝试对其进行调试。或者你可以尝试使用Visual Studio 2019 自己调试到Roslyn,它支持在反编译源中进行反编译和调试
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多