将新项添加到var类型的变量中 - 在一个变量中具有Constructor和Method声明

时间:2017-08-03 09:46:34

标签: c# .net roslyn

我有以下代码:

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;

在定义methodDeclarations的行之后,我还想添加一行,如下面的代码:

methodDeclarations.AddRange(
    memcls.DescendantNodes().OfType<ConstructorDeclarationSyntax>())

为了将返回的ConstructorDeclarationSyntax项添加到同一个methodDeclarations变量中;即,在同一个变量中同时包含ConstructorDeclarationSyntax和MethodDeclarationSyntax项。我怎样才能做到这一点?

我尝试了一些类型转换并使用List代替var for methodDeclarations变量,但是它在下一行中给出错误,我想在memmeth上访问ParameterList,Identifier和其他属性。

另一个技巧是,我尝试使用以下代码

var methodDeclarations = tree.GetRoot().DescendantNodes()
    .Where(c => c is MethodDeclarationSyntax || c is ConstructorDeclarationSyntax);

但是后来它再次在代码中访问ParameterList和Identifier属性时出错。

2 个答案:

答案 0 :(得分:1)

正如@Chris所提到的,如果共享基类没有你想要的适当属性,你应该使用两个单独的列表。

但是,如果确实想要使用单个列表,则以下内容将起作用:

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);
}

但请注意,由于需要进行反射,因此性能较差,并且您还会丢失通常由编译器提供的所有良好的静态语法检查。

答案 1 :(得分:1)

您可以按如下方式使用List<BaseMethodDeclarationSyntax>

var methods = new List<BaseMethodDeclarationSyntax>();
methods.AddRange(memcls.DescendantNodes().OfType<MethodDeclarationSyntax>());
methods.AddRange(memcls.DescendantNodes().OfType<ConstructorDeclarationSyntax>());

在该列表中的方法中,您可以使用BaseMethodDeclarationSyntax上声明的所有属性,包括

  • ExpressionBody
  • 参数列表
  • ...

但你是对的,那里没有Identifier属性,原因很简单:并非每个BaseMethodDeclarationSyntax都有一个。具有Identifier属性的是MethodDeclarationSyntax,ConstructorDeclarationSyntax和DestructorDeclarationSyntax。那些不是OperatorDeclarationSyntax和ConversionOperatorDeclarationSyntax。

但是,您可以轻松地从具有一个标识符的类型中获取标识符。可能最有效的方法是使用访问者:

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;
}

获得该课程后,您可以获得如下标识符:

foreach (var method in methods)
{
    var identifier = IdentifierVisitor.Instance.Visit(method);

    // for example:
    Console.WriteLine(identifier.Text);
}

这适用于常规方法,构造函数和析构函数。 在某种程度上,它也适用于运算符和转换运算符,除了那些将返回默认令牌。所以你可以这样做:

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);
    }
}

最后一件事:调用.DescendentNodes()将为您提供整个ClassDeclarationSyntax树中的每个节点。您应该使用.Members代替。这不会给你嵌套类中的方法(尽管如果你需要它们可以很容易地递归那些),但它会更有效率。