The syntax tree approach works well when you are only interested in specific pieces of syntax such as methods, classes, and throw statements, etc. However, sometimes you will need to find all nodes of a specific type within a syntax tree.
The CSharpSyntaxWalker
class allows you to create your syntax walker that can visit all nodes, and tokens, etc. We can simply inherit from CSharpSyntaxWalker
and override any particular method, as per your requirements to visit all nodes within the tree.
Now let's consider we have the following code to parse within a syntax tree.
SyntaxTree tree = CSharpSyntaxTree.ParseText(
@"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
namespace Parent
{
using Microsoft;
using System.ComponentModel;
namespace Child1
{
using Microsoft.Win32;
using System.Runtime.InteropServices;
class MyClass1 { }
}
namespace Child2
{
using System.CodeDom;
using Microsoft.CSharp;
class MyClass2 { }
}
}");
As you can see that in the source text, we have using
directives scattered across four different locations: the file-level, in the parent
namespace, and in the two nested namespaces.
Let's add a class and replace the following code.
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace RoslynDemo
{
class UsingWalker : CSharpSyntaxWalker
{
public readonly List<UsingDirectiveSyntax> Usings = new List<UsingDirectiveSyntax>();
public override void VisitUsingDirective(UsingDirectiveSyntax node)
{
if (node.Name.ToString() != "System" && !node.Name.ToString().StartsWith("System."))
{
this.Usings.Add(node);
}
}
}
}
We have added a public readonly
field in the UsingWalker
class to store the UsingDirectiveSyntax
nodes. In the VisitUsingDirective
method, it will add a node to the Usings
collection if Name
doesn't refer to the System
namespace or any of its descendant namespaces.
We can now create an instance of the UsingWalker
class and use that instance to visit the root of the parsed tree and iterate over the UsingDirectiveSyntax
nodes collected and print their names to the console.
static void SyntaxWalkerExample()
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(
@"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
namespace Parent
{
using Microsoft;
using System.ComponentModel;
namespace Child1
{
using Microsoft.Win32;
using System.Runtime.InteropServices;
class MyClass1 { }
}
namespace Child2
{
using System.CodeDom;
using Microsoft.CSharp;
class MyClass2 { }
}
}");
var root = (CompilationUnitSyntax)tree.GetRoot();
var walker = new UsingWalker();
walker.Visit(root);
foreach (var directive in walker.Usings)
{
Console.WriteLine(directive.Name);
}
}
Let's execute the above code and you will see the following output.
Microsoft.CodeAnalysis
Microsoft.CodeAnalysis.CSharp
Microsoft
Microsoft.Win32
Microsoft.CodeAnalysis.CSharp.Scripting
Microsoft.CSharp
Microsoft.CodeAnalysis.CSharp.Syntax
Microsoft.CodeAnalysis.Scripting