Reputation: 147
I have the following code:
var allclasses = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>();
foreach (var memcls in allclasses)
{
if (memcls != null)
{
var methodDeclarations = memcls.DescendantNodes().OfType<MethodDeclarationSyntax>();
foreach (var memmeth in methodDeclarations)
{
var paramDeclaratons = memmeth.ParameterList.Parameters;
Right after the line where methodDeclarations is defined, I also want to add a line like the following pseudo code:
methodDeclarations.AddRange(
memcls.DescendantNodes().OfType<ConstructorDeclarationSyntax>())
In order to have the ConstructorDeclarationSyntax items that are returned added to the same methodDeclarations variable as well; i.e., having both ConstructorDeclarationSyntax and MethodDeclarationSyntax items in the same variable. How can I do that?
I tried some type casting and using List instead of var for methodDeclarations variable, but then it gives errors on the next lines where I want to access ParameterList, Identifier and other properties on memmeth.
As another trick, I tried using the following code
var methodDeclarations = tree.GetRoot().DescendantNodes()
.Where(c => c is MethodDeclarationSyntax || c is ConstructorDeclarationSyntax);
But then again it gives error on accessing ParameterList and Identifier properties later in the code.
Upvotes: 3
Views: 165
Reputation: 10201
You can use a List<BaseMethodDeclarationSyntax>
as follows:
var methods = new List<BaseMethodDeclarationSyntax>();
methods.AddRange(memcls.DescendantNodes().OfType<MethodDeclarationSyntax>());
methods.AddRange(memcls.DescendantNodes().OfType<ConstructorDeclarationSyntax>());
On the methods in that list, you can use all the properties declared on BaseMethodDeclarationSyntax
, including
But you are right, there is no Identifier property there, for a very simple reason: not every BaseMethodDeclarationSyntax has one. The ones that do have an Identifier property are MethodDeclarationSyntax, ConstructorDeclarationSyntax and DestructorDeclarationSyntax. The ones that don't are OperatorDeclarationSyntax and ConversionOperatorDeclarationSyntax.
You can however easily get the Identifier from those types that have one. Probably the most efficient way to do so is by using a Visitor:
internal sealed class IdentifierVisitor : CSharpSyntaxVisitor<SyntaxToken>
{
public static IdentifierVisitor Instance { get; } = new IdentifierVisitor();
public override SyntaxToken VisitMethodDeclaration(MethodDeclarationSyntax node)
=> node.Identifier;
public override SyntaxToken VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
=> node.Identifier;
public override SyntaxToken VisitDestructorDeclaration(DestructorDeclarationSyntax node)
=> node.Identifier;
}
Once you have that class, you can get the identifier as follows:
foreach (var method in methods)
{
var identifier = IdentifierVisitor.Instance.Visit(method);
// for example:
Console.WriteLine(identifier.Text);
}
That would work for regular methods, constructors and destructors. In a way, it would work for operators and conversion operators too, except that those would return default tokens. So you could do something like:
var methods = memcls
.DescendantNodes()
.OfType<BaseMethodDeclarationSyntax>()
.ToList();
foreach (var method in methods)
{
var identifier = IdentifierVisitor.Instance.Visit(method);
if (!identifier.IsKind(SyntaxKind.None))
{
Console.WriteLine(identifier.Text);
}
}
One final thing: calling .DescendentNodes() will give you every node in the entire ClassDeclarationSyntax tree. You should probably use .Members instead. That would not give you the methods in nested classes (allthough you could easily get those recursively if you need them), but it would be much more efficient.
Upvotes: 1
Reputation: 7880
As mentioned by @Chris, if the shared base class does not have the appropriate properties you want, you should probably use two separate lists.
However, if you really want to use a single list, the following will work:
var methodDeclarations =
tree.GetRoot()
.DescendantNodes()
.Where(c => c is MethodDeclarationSyntax || c is ConstructorDeclarationSyntax)
.Cast<dynamic>();
foreach (var memmeth in methodDeclarations)
{
// Run-time checking so no syntax error
Console.WriteLine(memmeth.Identifier);
}
Note, however, that this will be less performant as reflection is required and you also lose all the nice static syntax checking that would normally be provided by the compiler.
Upvotes: 1