【问题标题】:Get a list of referenced Types within a project with Roslyn使用 Roslyn 获取项目中引用的类型列表
【发布时间】:2015-03-20 22:10:55
【问题描述】:

我想获取一个项目中所有使用类型的列表,例如:

var x = 1;
var y = x.ToString().GetType();

代码应返回System.Int32System.StringSystem.Type

我所拥有的太慢了...对于每个文件(语法树),我执行以下操作:

var root = await syntaxTree.GetRootAsync();
var nodes = root.DescendantNodes(n => true);

if (nodes != null)
{
    var syntaxNodes = nodes as SyntaxNode[] ?? nodes.ToArray();

    // IdentifierNameSyntax:
    //  - var keyword
    //  - identifiers of any kind (including type names)
    var namedTypes = syntaxNodes
        .OfType<IdentifierNameSyntax>()
        .Select(id => this.compilation
                  .GetSemanticModel(id.SyntaxTree)
                  .GetSymbolInfo(id)
                  .Symbol)
        .OfType<INamedTypeSymbol>()
        .ToArray();

    // add the found types to the list
    this.AddRange(namedTypes);

    // ExpressionSyntax:
    //  - method calls
    //  - property uses
    //  - field uses
    //  - all kinds of composite expressions
    var expressionSyntaxs = syntaxNodes
        .OfType<ExpressionSyntax>()
        .ToList();

    var typeSymbols = new List<ITypeSymbol>();
    for (int index = 0; index < expressionSyntaxs.Count; index++)
    {
        ExpressionSyntax ma = expressionSyntaxs[index];
        typeSymbols.Add(this.compilation
                    .GetSemanticModel(ma.SyntaxTree)
                    .GetTypeInfo(ma)
                    .Type);
    }

    var expressionTypes = typeSymbols
        .OfType<INamedTypeSymbol>()
        .ToArray();

    // add the found types to the list
    this.AddRange(expressionTypes);
}

动机:

我正在制作一个分析项目的工具,并告诉哪些 .Net 框架版本支持给定项目(例如便携式 .Net 框架)。

我希望在编译之前将一组使用的类型与一组可用的类型从框架中匹配起来会更快。

对于小文件,这段代码足够快,并且总时间少于编译所有可能的框架的时间......但是对于大文件,这是不可接受的。

有没有办法以可接受的方式获取类型列表?

【问题讨论】:

  • CPU 时间花在哪里?
  • 编译前需要这样做吗?也许查看编译后的 IL 并找到 methodref 和 typedef 标记会更容易。
  • @usr 大多数CPU时间在this.compilation.GetSemanticModel(x.SyntaxTree).GetSymbolInfo(x).Symbol
  • @KevinPilch-Bisson 我已经有一个默认编译。加载项目后,我调用this.compilation = await project.GetCompilationAsync()。从第一次编译开始,我检测使用的类型、匹配框架,然后对于每个匹配项,我使用从第一个版本更改为这个版本的引用进行构建。

标签: c# roslyn


【解决方案1】:

所以,我学到了一个教训:永远不要仅仅因为两个调用会返回相同的结果,就假设一个不可变结构会缓存它的任何计算。

确实,当方法是纯方法时,返回的结构总是相同的,但不一定是相同的实例。

我的假设导致我犯了一个错误:认为this.compilation.GetSemanticModel(id.SyntaxTree) 总是会为给定的SyntaxTree 返回语义模型的相同实例...这是不正确的。

我将代码更改为以下内容,现在速度非常快:

var root = await syntaxTree.GetRootAsync();
var nodes = root.DescendantNodes(n => true);

var st = root.SyntaxTree;
var sm = this.compilation.GetSemanticModel(st);

if (nodes != null)
{
    var syntaxNodes = nodes as SyntaxNode[] ?? nodes.ToArray();

    // IdentifierNameSyntax:
    //  - var keyword
    //  - identifiers of any kind (including type names)
    var namedTypes = syntaxNodes
        .OfType<IdentifierNameSyntax>()
        .Select(id => sm.GetSymbolInfo(id).Symbol)
        .OfType<INamedTypeSymbol>()
        .ToArray();

    this.Add(namedTypes);

    // ExpressionSyntax:
    //  - method calls
    //  - property uses
    //  - field uses
    //  - all kinds of composite expressions
    var expressionTypes = syntaxNodes
        .OfType<ExpressionSyntax>()
        .Select(ma => sm.GetTypeInfo(ma).Type)
        .OfType<INamedTypeSymbol>()
        .ToArray();

    this.Add(expressionTypes);
}

【讨论】:

  • 您可能为您访问的每个节点计算了整个语义模型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多