使用Roslyn获取项目中引用的类型列表

时间:2015-03-20 22:10:56

标签: c# roslyn

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

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框架版本支持给定的项目(例如Portable .Net Frameworks)。

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

对于小文件,这段代码足够快,并且总时间少于使用每个可能的框架进行编译的时间......但是对于大文件来说这是不可接受的。

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

1 个答案:

答案 0 :(得分:5)

所以,我已经吸取了教训:永远不会假设不可变结构会缓存任何计算,因为两次调用会返回相同的结果。

确实,当方法是纯粹的时,返回在结构上总是相同的,但它不必是相同的实例。

我的假设导致我犯了一个错误:认为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);
}