如何从MemberAccessExpressionSyntax获取方法体?

问题描述 投票:3回答:1

我正在使用Roslyn并尝试构建一个分析器,它将遍历方法的整个源(包括所有子方法),以便为用户提供洞察力。

对我来说,如何进入MemberAccessExpression(指向该方法)以便我可以开始浏览包含该方法的文件是不明智的。

我已经尝试迭代ChildNodesChildTokens,我也查看了调试器中的对象,我看到没有可用于加载MemberAccessExpression引用的类/方法的属性。

在遍历树以找到我有兴趣探索的方法之后,我执行以下操作:

private void AnalyzeSyntaxNode(SyntaxNode syntaxNode)
{
    if (this.foundMethod)
    {
        return;
    }

    if (syntaxNode is InvocationExpressionSyntax invocationExpressionSyntax)
    {
        this.foundMethod = true;
        PrintNodeAndChildren(invocationExpressionSyntax);
    }

    var children = syntaxNode.ChildNodes();

    foreach (var child in children)
    {
        AnalyzeSyntaxNode(child);
    }
}

private void PrintNodeAndChildren(SyntaxNode node)
{
    Console.WriteLine($"Child: {node}");

    var children = node.ChildNodes();

    if (children.Any())
    {
        foreach (var child in children)
        {
            PrintNodeAndChildren(child);
        }
    }
}

我想访问包含方法的类,以及方法本身的主体。

在下面的例子中,我开始遍历Caller的SyntaxTree,我想访问Callee.DoSomethingElse的主体。实际上,我的目标是用_visited = true中的_visited = 99替换Callee.DoSomethingElse,但是如果我能弄清楚如何访问树的那一部分,我觉得我可以自己替换节点。

Caller.cs

public class Caller
{
    private Callee _callee = new Callee();
    public void DoSomething()
    {
        _callee.DoSomethingElse();
    }
}

Callee.cs (Different project to Caller.cs)

public class Callee
{
    private int _visited = 0;
    public void DoSomethingElse()
    {
        _visited = 1;
    }
}

这是一个任意的,荒谬的例子,但我觉得好像它得到了重点。

c# abstract-syntax-tree roslyn
1个回答
0
投票

事实证明,用于此的正确类是CSharpSyntaxRewriterCSharpSyntaxVisitor的子类,它可以使语法树的导航变得更加容易。

它在功能上与CSharpSyntaxVisitor的工作方式相同,但允许修改语法节点。

这是我的类的实现:

public class AssignmentReplacer : CSharpSyntaxRewriter
{
    private string inMethod;

    public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        inMethod = node.Identifier.ValueText;

        return base.VisitMethodDeclaration(node);
    }

    public override SyntaxNode VisitAssignmentExpression(AssignmentExpressionSyntax node)
    {
        if (inMethod == "DoSomethingElse")
        {
            if (node.Left is IdentifierNameSyntax name &&
               name.Identifier.Text == "_visited")
            {

                return node.Update(node.Left,
                                   node.OperatorToken,
                                   SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression));
            }
        }

        return base.VisitAssignmentExpression(node);
    }
}

当它以深度优先于树行走时,它会在私有字段中从VisitMethodDeclaration跟踪其当前方法位置。它在VisitAssignmentExpression中使用它来确保它在修改节点时使用正确的方法。

一旦它在正确的方法中识别出AssignmentExpressionSyntax节点,它就会创建一个节点的修改副本,右侧更改为新的LiteralExpression

您可能需要在此周围添加更多检查以确保修改正确的节点,但这适用于您的简单示例数据。

你可以使用这样的类:

var programText =
@"public class Callee
{
    private bool _visited = false;
    public void DoSomethingElse()
    {
        _visited = false;
    }
}";

var tree = CSharpSyntaxTree.ParseText(programText);
var root = tree.GetRoot();

var visitor = new AssignmentReplacer();
var updated = visitor.Visit(root);

运行访问者后,updated将包含修改后的代码:

public class Callee
{
    private bool _visited = true;
    public void DoSomethingElse()
    {
        _visited = true;
    }
}

同样谨慎,这是我第一次做这些,所以它可能不是最有效或最好的方法。

感谢您提出有趣的挑战!

可以找到一个完整工作的LINQPad示例here

© www.soinside.com 2019 - 2024. All rights reserved.