C# SyntaxRewriter.Visit*不会访问类型为的所有节点*

C# SyntaxRewriter.Visit*不会访问类型为的所有节点*,c#,roslyn,C#,Roslyn,已解决:mike z是对的,我没有正确调用基继续递归。谢谢,迈克 我正在使用Roslyn进行一些代码重写,通过实现一个SyntaxRewriter 我遇到的一件奇怪的事情是,当重写SyntaxNode.visitinIvocationExpression(InvocationExpressionSyntax)时,它不会访问树中的所有InvocationExpressionSyntax节点。(我假设所有SyntaxNode类型都是相同的) 例如,给定此调用表达式: controller.Add

已解决:mike z是对的,我没有正确调用基继续递归。谢谢,迈克

我正在使用Roslyn进行一些代码重写,通过实现一个
SyntaxRewriter

我遇到的一件奇怪的事情是,当重写
SyntaxNode.visitinIvocationExpression(InvocationExpressionSyntax)
时,它不会访问树中的所有
InvocationExpressionSyntax
节点。(我假设所有
SyntaxNode
类型都是相同的)

例如,给定此调用表达式:

  controller.Add(5, 6).ToString();
它只访问整个表达式的节点,即使其中有两个调用

虽然我当然可以编写递归函数或类似函数来解析子/嵌套调用表达式节点,但这似乎不一致且不方便。
为什么它不访问整个树中所有*类型的节点

这是我的超控:

    public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
    {
        IdentifierNameSyntax ident = node.ChildNodes().OfType<IdentifierNameSyntax>().FirstOrDefault();
        if (ident == null)
            return node;//In my test case, the example above returns here when it's node is encountered.  Shouldn't this then allow the walker to continue deeper into the node,
                        // finding the deeper nested Invocations?

        string name = ident.PlainName;
        if (!TempStore.ConstructedInvocations.ContainsKey(name))//not replacing this then
            return node;

        InvocationExpressionSyntax newInvocation = ((InvocationExpressionSyntax)TempStore.ConstructedInvocations[name]).WithArgumentList(node.ArgumentList);
        return newInvocation;
    }
public覆盖SyntaxNode VisitionExpression(InvocationExpressionSyntax节点)
{
IdentifierNameSyntax ident=node.ChildNodes()。of type().FirstOrDefault();
if(ident==null)
return node;//在我的测试用例中,当遇到上面的示例的节点时,返回到这里。这不应该允许walker继续深入到节点中吗,
//查找更深层次的嵌套调用?
字符串名称=ident.PlainName;
如果(!TempStore.ConstructedInvocations.ContainsKey(name))//不替换此项,则
返回节点;
InvocationExpressionSyntax newInvocation=((InvocationExpressionSyntax)TempStore.ConstructedInvocations[name]),带有ArgumentList(node.ArgumentList);
返回新调用;
}

在调试中单步执行该代码确认
controller.Add(5,6).ToString()的
InvocationExpressionNode
中确实嵌套了子
调用ExpressionNodes

我不知道Roslyn API,但是对于表达式树(
ExpressionVisitor
),您需要调用基本方法来继续访问树

这是因为您的方法可能希望停止访问树。如果您想停止,就不能调用基本方法


这似乎支持了这个理论。

我在玩Roslyn API时遇到了类似的问题。在Visit*方法的基类实现中递归调用Visit方法。一旦覆盖其中一个节点,您将负责访问所有子节点。您可以通过在要重写的节点上调用base.Visit*方法或在每个子节点上调用Visit来实现这一点

下面是一些示例代码,我在其中通过交换逻辑运算符&和| |来重写它们。我使用不同的运算符构造一个新节点,然后调用base.VisitBinaryExpression以确保访问所有子节点。否则,我们将只重写表达式的一部分,如
var1&&(var2 | | var3)
。另一个可能的实现是调用node.Left和node.Right上的Visit,然后根据这些结果构造新节点

public class LogicalOperatorRewriter : SyntaxRewriter
{
    public override SyntaxNode VisitBinaryExpression(BinaryExpressionSyntax node)
    {
        SyntaxKind newExpressionKind = GetNewKind(node.Kind);
        BinaryExpressionSyntax newNode = (BinaryExpressionSyntax)Syntax.BinaryExpression(newExpressionKind, left: node.Left, right: node.Right).Format().GetFormattedRoot();
        return base.VisitBinaryExpression(newNode);
    }

    private SyntaxKind GetNewKind(SyntaxKind kind)
    {
        switch (kind)
        {
            case SyntaxKind.LogicalAndExpression:
                return SyntaxKind.LogicalOrExpression;
            case SyntaxKind.LogicalOrExpression:
                return SyntaxKind.LogicalAndExpression;
            default: return kind;
        }
    }
}

你的密码在哪里?您正在调用基类方法吗?如果您不这样做,它将不会访问子节点。@mikez您的评论似乎解决了这个问题。你能把它作为一个答案(最好是解释为什么需要调用基方法)。这是正确的答案,我只是对树遍历/递归不够熟悉,看不出问题出在哪里。不过,为了公平起见,我打算给迈克一个机会,把他的评论作为回答,因为他是正确的,而且是第一个。你的暗示让我尝试沃克课程,而不是访客。我还没有弄清楚确切的区别,但是你的建议让我的代码起作用了。谢谢